curl --request POST \
--url https://app.base44.com/api/apps/{app_id}/google-ads/tracking-scan/build-bootstrap-prompt \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"wire_events": true
}
'import requests
url = "https://app.base44.com/api/apps/{app_id}/google-ads/tracking-scan/build-bootstrap-prompt"
payload = { "wire_events": True }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({wire_events: true})
};
fetch('https://app.base44.com/api/apps/{app_id}/google-ads/tracking-scan/build-bootstrap-prompt', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.base44.com/api/apps/{app_id}/google-ads/tracking-scan/build-bootstrap-prompt",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'wire_events' => true
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.base44.com/api/apps/{app_id}/google-ads/tracking-scan/build-bootstrap-prompt"
payload := strings.NewReader("{\n \"wire_events\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.base44.com/api/apps/{app_id}/google-ads/tracking-scan/build-bootstrap-prompt")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"wire_events\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.base44.com/api/apps/{app_id}/google-ads/tracking-scan/build-bootstrap-prompt")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"wire_events\": true\n}"
response = http.request(request)
puts response.read_body{
"composer_prompt": "Wire up Google Ads conversion tracking for this app.\n\nCall get_capability_guide(\"google_ads\") first, then follow it.",
"conversion_id": "AW-123456789",
"actions_created_or_existing": 10,
"wired_mappings": 3,
"skipped_reason": "no_google_ads_account",
"next_step": "Create the Google Ads account first with POST /accounts/ensure-for-tracking (no campaign, nothing charged), then call this again.",
"tracking_snippet": "if (typeof document !== 'undefined' && !window.__gads_loaded) {\n window.__gads_loaded = true;\n ...\n window.gtag('config', 'AW-123456789', { send_page_view: false });\n}",
"conversions": [
{
"mapping_id": "68b1c0d4e7b91d003c45a1f7",
"conversion_type": "PURCHASE",
"entity_name": "Order",
"trigger_action": "create",
"send_to": "AW-123456789/AbCdEfGhIj",
"example_call": "window.gtag('event', 'conversion', { send_to: 'AW-123456789/AbCdEfGhIj' });"
}
]
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>"
}
]
}Build Google Ads tracking fix prompt
Creates the app’s Google Ads conversion goals if it does not have them, then returns an instruction that tells the AI how to install conversion tracking in the app.
Send the returned composer_prompt to Send chat message and the AI does the wiring for you. Publish the app afterwards, because Google Ads only records conversions from the published app. Pair it with Scan Google Ads conversion tracking to see what needs fixing first.
This call changes the Google Ads account. Any goal missing from the standard set is created on it, covering PURCHASE, ADD_TO_CART, BEGIN_CHECKOUT, SIGNUP, SUBMIT_LEAD_FORM, CONTACT, REQUEST_QUOTE, BOOK_APPOINTMENT, SUBSCRIBE_PAID, and PHONE_CALL_LEAD. Creating a goal is not reversible through this API. Read List Google Ads conversion actions first if you want to know what the account already has.
Send wire_events as true to get a prompt that also wires each of the account’s enabled conversion mappings, instead of only installing the base tag. On an account that has enabled mappings, that path waits for Google to publish the tag for each mapped goal and retries the read up to four times about a second apart, so the request can take several seconds.
If you write the app’s code yourself instead of through the chat, read tracking_snippet and conversions rather than parsing the prompt. Install tracking_snippet once, then fire each entry’s send_to where its action succeeds, and publish.
skipped_reason set to no_google_ads_account and a next_step. Launch first. Campaigns launched before tracking is verified start paused. Then call this again, install the snippet and the conversion calls, and publish. The campaigns resume on their own once the published app carries one of the returned send_to values.wire_events is supported in the body. The builder UI sends a snapshot of the app’s source alongside it, which is internal and not part of this API.curl --request POST \
--url https://app.base44.com/api/apps/{app_id}/google-ads/tracking-scan/build-bootstrap-prompt \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"wire_events": true
}
'import requests
url = "https://app.base44.com/api/apps/{app_id}/google-ads/tracking-scan/build-bootstrap-prompt"
payload = { "wire_events": True }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({wire_events: true})
};
fetch('https://app.base44.com/api/apps/{app_id}/google-ads/tracking-scan/build-bootstrap-prompt', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.base44.com/api/apps/{app_id}/google-ads/tracking-scan/build-bootstrap-prompt",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'wire_events' => true
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.base44.com/api/apps/{app_id}/google-ads/tracking-scan/build-bootstrap-prompt"
payload := strings.NewReader("{\n \"wire_events\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.base44.com/api/apps/{app_id}/google-ads/tracking-scan/build-bootstrap-prompt")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"wire_events\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.base44.com/api/apps/{app_id}/google-ads/tracking-scan/build-bootstrap-prompt")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"wire_events\": true\n}"
response = http.request(request)
puts response.read_body{
"composer_prompt": "Wire up Google Ads conversion tracking for this app.\n\nCall get_capability_guide(\"google_ads\") first, then follow it.",
"conversion_id": "AW-123456789",
"actions_created_or_existing": 10,
"wired_mappings": 3,
"skipped_reason": "no_google_ads_account",
"next_step": "Create the Google Ads account first with POST /accounts/ensure-for-tracking (no campaign, nothing charged), then call this again.",
"tracking_snippet": "if (typeof document !== 'undefined' && !window.__gads_loaded) {\n window.__gads_loaded = true;\n ...\n window.gtag('config', 'AW-123456789', { send_page_view: false });\n}",
"conversions": [
{
"mapping_id": "68b1c0d4e7b91d003c45a1f7",
"conversion_type": "PURCHASE",
"entity_name": "Order",
"trigger_action": "create",
"send_to": "AW-123456789/AbCdEfGhIj",
"example_call": "window.gtag('event', 'conversion', { send_to: 'AW-123456789/AbCdEfGhIj' });"
}
]
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>"
}
]
}Authorizations
Personal access token, sent as Authorization: Bearer <token>. Each operation lists the permission it needs: read_only or full_access. A full-access token can call every operation. A read-only token can call only the operations that list read_only; anything else returns 403 read_only_credential_route_not_allowed.
Path Parameters
ID of the app whose Google Ads conversion tracking you want to manage.
Body
Whether the prompt should also wire the account's enabled conversion mappings (true) or only install the base tag (false).
true
Response
The prompt to send to the AI.
A ready-made instruction for the AI to install conversion tracking.
The instruction to send to Send chat message. It ends with a reminder to publish, because Google Ads only records conversions from the published app. The value is an empty string when skipped_reason is set.
"Wire up Google Ads conversion tracking for this app.\n\nCall get_capability_guide(\"google_ads\") first, then follow it."
The gtag conversion ID baked into the prompt, or an empty string when the prompt tells the AI to look each one up instead of carrying a concrete ID. Absent when skipped_reason is set.
"AW-123456789"
How many conversion goals the account has after this call, counting the ones it already had. Absent when skipped_reason is set.
10
How many of the account's conversion mappings the prompt carries wiring guidance for. When you send wire_events and the account has no enabled mapping, this reports the number of conversion categories the prompt offers instead, so read any value above 0 as a sign that the prompt carries wiring guidance rather than as a mapping count. Absent when skipped_reason is set.
3
Why no prompt was produced. The only value is no_google_ads_account, which means the app has no Google Ads account that can carry conversion tracking. The field is absent whenever a prompt was produced.
"no_google_ads_account"
Present only with skipped_reason. What to do next, in plain words. When the app has no account yet it points to Create Google Ads account for tracking, which creates the account the conversion ID belongs to without launching anything. When the account exists but is held, for example on billing, it names the account's status instead.
"Create the Google Ads account first with POST /accounts/ensure-for-tracking (no campaign, nothing charged), then call this again."
The gtag bootstrap to install in the app, as plain JavaScript. Use this one, not the snippet from Get Google Ads tracking script: it is the same bootstrap composer_prompt installs. Put it at module scope at the top of the app's root file, outside any component and not inside a useEffect, so window.gtag exists before a page fires a conversion. When the app owner chose to track visitors only after consent, it starts every consent type as denied and the app needs a consent banner that sends gtag('consent', 'update', ...). An empty string when no conversion ID is available yet, which is usually the case for wire_events set to true on an account with no enabled mapping, so send false there. Absent when skipped_reason is set.
"if (typeof document !== 'undefined' && !window.__gads_loaded) {\n window.__gads_loaded = true;\n ...\n window.gtag('config', 'AW-123456789', { send_page_view: false });\n}"
One entry per enabled conversion mapping whose goal has a published tag, with the exact send_to to fire. A mapping whose tag Google has not published yet is left out, so call again shortly if one is missing. Empty when the account has no enabled mapping: create one, or fire conversion_id/conversion_label from List Google Ads conversion actions. Absent when skipped_reason is set.
Show child attributes
Show child attributes
Was this page helpful?