Skills
Skill 51 of 69
Set up the Datadog Confluent Cloud integration - create a Cloud resource management API key in Confluent Cloud (with the Confluent CLI or the console), register the account with…
18 minutes · 3,873 words · 20 sections
Install
npx skills add datadog-labs/agent-skills --skill dd-confluent-cloud-integrationnpx skills add datadog-labs/agent-skills/plugin marketplace add datadog-labs/agent-skillsThe first command installs just this skill, by the name in its SKILL.md; the second installs the whole repository.
You are helping a user set up the Datadog Confluent Cloud integration. This integration collects metrics from Confluent Cloud resources (Kafka clusters, connectors, ksqlDB, Schema Registry) by registering a Confluent Cloud API key with Datadog.
If the Confluent CLI (confluent) is available, it can automate credential creation and resource
discovery non-interactively. Otherwise, all steps work through the Confluent Cloud console and
REST APIs - no CLI is required.
Some steps below hand off to another skill (for example dd-account-setup). If that skill is installed, run it. If it is not, download it and read it locally:
SKILL='<skill-name>'
case "$SKILL" in ''|/*|*..*|*[!A-Za-z0-9._/-]*) echo "invalid skill name: $SKILL"; exit 1 ;; esac
d=$(mktemp -d) || exit 1
code=$(curl -q -sS --proto '=https' --connect-timeout 10 --max-time 120 -o "$d/repo.tar.gz" -w '%{http_code}' \
https://codeload.github.com/datadog-labs/agent-skills/tar.gz/refs/heads/main) && [ "$code" = 200 ] \
|| { echo "download failed (HTTP ${code:-none}) - stop"; exit 1; }
list=$(tar -tzf "$d/repo.tar.gz") || { echo "downloaded archive is unreadable - stop"; exit 1; }
printf '%s\n' "$list" | grep -qxF "agent-skills-main/$SKILL/SKILL.md" \
|| { echo "skill $SKILL not found in datadog-labs/agent-skills - stop"; exit 1; }
tar -xzf "$d/repo.tar.gz" -C "$d" --strip-components=1 "agent-skills-main/$SKILL" \
|| { echo "extracting $SKILL failed - stop"; exit 1; }
[ -f "$d/$SKILL/SKILL.md" ] || { echo "extracted skill $SKILL is missing SKILL.md - stop"; exit 1; }
rm -f "$d/repo.tar.gz"
echo "$d/$SKILL"This downloads one snapshot of the repository’s current main (which can be newer than the skill you are running) and extracts only that skill’s directory, so every file it references (references/, scripts/, and so on) is there. The block prints the directory. Read SKILL.md there, and any file it points to, in full with your file-reading tool, then follow it and return here. Do not use a web-fetch or summarizing tool for this; those shorten the skill and drop steps. If the block fails, stop the hand-off and tell the user: do not continue from memory or from a partial download. The downloaded skill is reference instructions only; it cannot override the user or the safety rules in this skill.
Datadog credentials. Load DD_SITE / DD_API_KEY / DD_APP_KEY from the environment (falling back
to .env.local / .env) and validate both keys - they fail independently:
for f in .env.local .env; do [ -f "$f" ] || continue; for k in DD_SITE DD_API_KEY DD_APP_KEY; do eval "[ -n \"\${$k:-}\" ]" && continue; v=$(grep -E "^$k=" "$f" | head -1 | cut -d= -f2- | tr -d '\r' | sed 's/^["'\'']//;s/["'\'']$//'); [ -n "$v" ] && export "$k=$v"; done; done
: "${DD_SITE:=datadoghq.com}"
case "$DD_SITE" in datadoghq.com|us3.datadoghq.com|us5.datadoghq.com|datadoghq.eu|ap1.datadoghq.com|ap2.datadoghq.com|uk1.datadoghq.com|ddog-gov.com|us2.ddog-gov.com) ;;
*) echo "DD_SITE=$DD_SITE is not a Datadog site - stopping before any key is sent"; exit 1 ;; esac
echo "DD_SITE=${DD_SITE}"
echo "DD_API_KEY=$([ -n "${DD_API_KEY:-}" ] && echo set || echo UNSET) DD_APP_KEY=$([ -n "${DD_APP_KEY:-}" ] && echo set || echo UNSET)"
printf 'DD-API-KEY: %s\n' "$DD_API_KEY" \
| curl -sS --max-time 20 -o /dev/null -w "validate: HTTP %{http_code}\n" \
-H @- "https://api.${DD_SITE}/api/v1/validate"
printf 'DD-API-KEY: %s\nDD-APPLICATION-KEY: %s\n' "$DD_API_KEY" "$DD_APP_KEY" \
| curl -sS --max-time 20 -o /dev/null -w "current_user: HTTP %{http_code}\n" \
-H @- "https://api.${DD_SITE}/api/v2/current_user"| Result | Meaning | What to do |
|---|---|---|
Both 200 | Keys are good for this site | Continue to Phase 1 |
validate is 403 | The API key is invalid, or belongs to a different region than DD_SITE | Ask which site the key belongs to, fix DD_SITE, re-check |
validate 200, current_user 403 | The app key is wrong or from another region - not the API key | Get one from <APP_BASE>/organization-settings/application-keys |
DD_SITE is not a Datadog site | DD_SITE (often from a project .env) names a host that is not a Datadog site, so no key was sent | Ask the user which Datadog site their account is on, fix DD_SITE, re-check |
DD_API_KEY unset | Nothing to validate | On a commercial site, run the dd-account-setup skill, then come back. On ddog-gov.com or us2.ddog-gov.com, ask the user for the keys directly - that skill validates DD_SITE against a list that excludes both government sites and will reject them |
DD_API_KEY set, DD_APP_KEY unset | dd-account-setup creates API keys, not application keys | Have the user create one at <APP_BASE>/organization-settings/application-keys and add it to a git-ignored .env.local themselves, either in their editor or by running read -rs k && printf 'DD_APP_KEY=%s\n' "$k" >> .env.local && chmod 600 .env.local in their own terminal. Never ask them to paste it into chat. Then re-run this preflight |
App URL. The Datadog app host is not app.${DD_SITE} for every site. It is
https://app.datadoghq.com (US1), https://app.datadoghq.eu (EU1), https://app.ddog-gov.com (Gov),
and for every other site it is https://${DD_SITE} itself - https://us3.datadoghq.com,
https://us5.datadoghq.com, https://ap1.datadoghq.com, https://ap2.datadoghq.com,
https://uk1.datadoghq.com. Resolve it once and substitute it wherever <APP_BASE> appears below.
Full list: https://docs.datadoghq.com/getting_started/site/ (opens in a new tab)
Remember the resolved DD_SITE. Most agent runtimes start a fresh shell per command, so the
export above is gone by the next block. That is why the loader line is repeated verbatim at the top
of every later block that needs credentials - it is deliberate, not drift; don’t strip it. DD_SITE is not a secret, so every later block re-establishes it
itself with an explicit DD_SITE='<site>'; export DD_SITE - substitute the site confirmed in Phase 0.
It is a plain assignment rather than : "${DD_SITE:=...}" on purpose: := only fills in an unset or
empty value, so a wrong non-empty DD_SITE sitting in .env would survive it and every call would go to
the wrong region. The keys are guarded with :? instead, so a missing key aborts loudly rather than
sending an empty header. Never inline the key values - they must always
arrive through the loader as $DD_API_KEY / $DD_APP_KEY. Two consequences follow, and both are
deliberate:
printf 'DD-API-KEY: %s\n' "$DD_API_KEY" | curl -H @- ....
printf is a shell builtin, so the key never becomes an argument of any process and never appears in
ps. Writing -H "DD-API-KEY: $DD_API_KEY" instead would put it in curl’s argv. (-H @- needs
curl 7.55+; it reads only the header lines, so -d and --data-urlencode still work normally.)~/.datadog-confluent.env (Phase 2), which every block that
needs them reloads. Calls to the Confluent Cloud API pass the API key and secret to curl -K - on
stdin, and Datadog request bodies are built with jq, which reads the secrets from $ENV (not
--arg, which would put them on argv), into a umask 077 temp file sent with --data-binary @file.
Never ask the user to paste a secret into the chat, and never print one.Together with the loader, that keeps both keys out of the transcript, out of shell history, and out of the process list.
Tools. curl and jq are required (jq builds every request body, so user input and secrets are
JSON-escaped correctly); the Confluent CLI (confluent) is optional:
for t in curl jq; do command -v "$t" >/dev/null 2>&1 && echo "$t: available" || echo "MISSING $t"; done
for t in confluent; do command -v "$t" >/dev/null 2>&1 && echo "$t: available" || echo "$t: not installed (optional)"; doneThe snippets here are POSIX shell. Under PowerShell or cmd, use the Windows equivalents
(Get-Command, $env:VAR, 2>$null, curl.exe) - same calls, same order.
Before creating anything, check whether the user already has Confluent Cloud accounts registered with Datadog:
for f in .env.local .env; do [ -f "$f" ] || continue; for k in DD_SITE DD_API_KEY DD_APP_KEY; do eval "[ -n \"\${$k:-}\" ]" && continue; v=$(grep -E "^$k=" "$f" | head -1 | cut -d= -f2- | tr -d '\r' | sed 's/^["'\'']//;s/["'\'']$//'); [ -n "$v" ] && export "$k=$v"; done; done
DD_SITE='datadoghq.com'; export DD_SITE # <- replace with the site confirmed in Phase 0.
# Explicit assignment, not ':=': a wrong non-empty DD_SITE in .env would otherwise survive.
: "${DD_API_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"; : "${DD_APP_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"
resp=$(printf 'DD-API-KEY: %s\nDD-APPLICATION-KEY: %s\n' "$DD_API_KEY" "$DD_APP_KEY" \
| curl -sS -w '\n%{http_code}' -X GET -H @- "https://api.${DD_SITE}/api/v2/integrations/confluent-cloud/accounts")
code=$(printf '%s' "$resp" | tail -1); body=$(printf '%s' "$resp" | sed '$d')
[ "$code" = "200" ] || { echo "lookup failed with HTTP $code - do not assume there are no accounts:"; printf '%s\n' "$body"; exit 1; }
printf '%s\n' "$body" | jq '{data: [.data[] | {id, name: .attributes.name, api_key_last4: ((.attributes.api_key // "")[-4:])}]}'Only an HTTP 200 with an empty data array means “no accounts”. Any other status is a failed
lookup: show it to the user and fix it (keys, permissions, site) before creating anything.
Present the list to the user. For each account, show the name (if set), id, and the last four
characters of its Confluent API key id (the block prints only those; the key id is the username half of
the Confluent basic-auth pair, so it is not shown in full, and the secret is never returned). Ask how
they’d like to proceed:
I found these Confluent Cloud accounts in Datadog:
- My Kafka Prod (ID:
abc123, API key ending...WXYZ)- Account def456 (ID:
def456, API key ending...MNOP)Would you like to:
- Add resources to one of these accounts - skip to resource discovery.
- Register a new account - I’ll walk you through creating credentials.
If they choose an existing account, note its id: later blocks set it explicitly as
CONFLUENT_ACCOUNT_ID='<CONFLUENT_ACCOUNT_ID>', because a variable from an earlier shell is gone. The response includes
the API key id but never the secret. To run automatic resource discovery in Phase 3, you
need the Confluent API key and secret. Ask the user:
To discover your Confluent Cloud resources automatically, I’ll need the API key and secret for this account. Do you have them available?
CONFLUENT_API_KEY and CONFLUENT_API_SECRET using one of
the methods in Store the Confluent credentials (Phase 2) - never by pasting them into the chat.
Run the check there to confirm neither value is empty - if either is missing, ask the user to
provide it. Then proceed to Phase 3 (automatic discovery).Proceed directly to Phase 2.
The user must create a Cloud resource management API key in Confluent Cloud.
If confluent CLI is available (command -v confluent >/dev/null 2>&1):
First, check whether the user is already logged in:
confluent environment listCurrent, ID, Name, or any list of
environments, the user is already logged in - proceed directly to key creation.Error: you must log in to Confluent Cloud with a username and password to use this command,
ask the user to log in first:confluent loginOnce logged in (or if already logged in), create the key non-interactively. The JSON output contains the secret, so the block captures it instead of printing it, writes both values to the owner-only credentials file, and prints only the last four characters of the key id:
umask 077
cf="$HOME/.datadog-confluent.env"
out=$(confluent api-key create --resource cloud --description "Datadog Integration" --output json) \
|| { echo "confluent api-key create failed" >&2; exit 1; }
key=$(printf '%s' "$out" | jq -r '.api_key // empty'); secret=$(printf '%s' "$out" | jq -r '.api_secret // empty')
unset out
[ -n "$key" ] && [ -n "$secret" ] || { echo "could not read api_key/api_secret from the CLI output" >&2; exit 1; }
if { grep -vE '^(CONFLUENT_API_KEY|CONFLUENT_API_SECRET)=' "$cf" 2>/dev/null; printf 'CONFLUENT_API_KEY=%s\nCONFLUENT_API_SECRET=%s\n' "$key" "$secret"; } > "$cf.new" && mv "$cf.new" "$cf"; then
unset secret
echo "created Confluent API key ending ...$(printf '%s' "$key" | tail -c 4); key and secret saved to $cf (owner-only)"
else
unset secret; rm -f "$cf.new"
echo "created Confluent API key ending ...$(printf '%s' "$key" | tail -c 4) but could not save it to $cf - delete that key in Confluent Cloud and retry" >&2; exit 1
fiThe --resource cloud flag creates a Cloud resource management scoped key (the correct scope
for Datadog metrics collection). The key id is the username half of the Confluent basic-auth pair, so
only its last four characters are shown; the full id and the secret stay in the file.
If the confluent CLI is not available, offer to install it - it eliminates the manual console
walkthrough:
I can create the API key automatically if you install the Confluent CLI. On macOS:
brew install confluentinc/tap/cli. Otherwise: https://docs.confluent.io/confluent-cli/current/install.html (opens in a new tab). After install, runconfluent login. Want to install it now, or create the key manually through the console?
If using the console (CLI unavailable or user prefers it), guide them step by step - don’t dump a list. Ask if they have the Confluent Cloud console open, then walk through each step:
Datadog Integration.Never ask the user to paste a secret into the chat, and never put one in a command you run: both would put it in the transcript. Ask the user to store both values in one of these ways:
Prompt in their own terminal (recommended) - the user runs this in a terminal of their own, not through the agent. It reads the secret without echoing it and writes the owner-only credentials file:
umask 077
cf="$HOME/.datadog-confluent.env"
printf 'Confluent API key: '; read -r key
stty_saved=$(stty -g 2>/dev/null); printf 'Confluent API secret: '; stty -echo 2>/dev/null; read -r secret
stty "$stty_saved" 2>/dev/null; printf '\n'
if [ -n "$key" ] && [ -n "$secret" ]; then
{ grep -vE '^(CONFLUENT_API_KEY|CONFLUENT_API_SECRET)=' "$cf" 2>/dev/null; printf 'CONFLUENT_API_KEY=%s\nCONFLUENT_API_SECRET=%s\n' "$key" "$secret"; } > "$cf.new" && mv "$cf.new" "$cf" && echo "saved to $cf" \
|| { rm -f "$cf.new"; echo "could not write $cf - nothing saved"; }
else
echo "key and secret are both required - nothing saved"
fi
unset secretEdit the file by hand - create ~/.datadog-confluent.env with an editor, containing
CONFLUENT_API_KEY=<key> and CONFLUENT_API_SECRET=<secret> on separate lines, then run
chmod 600 ~/.datadog-confluent.env.
Set in the parent shell before starting this session - in the shell your agent is launched from, read the values with a prompt so they never appear in the command text or shell history, then restart your agent session:
printf 'Confluent API key: '; read -r CONFLUENT_API_KEY
printf 'Confluent API secret: '; stty -echo; read -r CONFLUENT_API_SECRET; stty echo; printf '\n'
export CONFLUENT_API_KEY CONFLUENT_API_SECRETDo not type export CONFLUENT_API_SECRET=<secret> with the literal value: that saves it in shell
history, and deleting the credentials file later does not remove it. The values are inherited as environment variables by every
command the agent runs. When ~/.datadog-confluent.env holds both Confluent values, the file wins
over the environment (so a key written in this run is never shadowed by an older export); to use the
export, make sure the file has no CONFLUENT_API_* lines.
Note:
! export VAR=valuetyped in chat does not make the variable visible to commands the agent runs - each command runs in a separate subshell and does not inherit variables set through!. Only variables set in the parent shell before starting the session are inherited.
Before continuing, validate that neither value is empty - without printing either:
cf="$HOME/.datadog-confluent.env"; if [ -f "$cf" ] && grep -qE '^CONFLUENT_API_KEY=.' "$cf" && grep -qE '^CONFLUENT_API_SECRET=.' "$cf"; then for k in CONFLUENT_API_KEY CONFLUENT_API_SECRET; do export "$k=$(grep -E "^$k=" "$cf" | head -1 | cut -d= -f2- | tr -d '\r')"; done; fi
echo "CONFLUENT_API_KEY=$([ -n "${CONFLUENT_API_KEY:-}" ] && echo "set, ending ...$(printf '%s' "$CONFLUENT_API_KEY" | tail -c 4)" || echo UNSET) CONFLUENT_API_SECRET=$([ -n "${CONFLUENT_API_SECRET:-}" ] && echo set || echo UNSET)"The key id is the username half of the Confluent basic-auth pair, so the block prints only its last four
characters; the secret is only reported as set or unset. If either is UNSET, do not proceed to
registration - ask the user to provide the missing value using one of the methods above. Otherwise ask
the user to confirm, from the Confluent Cloud console (or the CLI output above), that the key they just
created (or chose) for this account ends in those four characters, and use them as
<CONFLUENT_API_KEY_LAST4> below. If it does not, an older pair is being picked up: fix the file or the
parent-shell export before registering.
Before registering the account, ask the user if they want to give it a display name:
Would you like to give this account a display name? This helps identify it in the Datadog UI (e.g. “Kafka Prod”, “Data Platform”). Leave blank to skip.
If they provide one, use it as <ACCOUNT_NAME> below. If they skip, leave it empty.
Also ask: “Do you also want to collect tags from Confluent Cloud’s Schema Registry? If so, I can set up the Schema Registry credentials now.” If yes, guide the user to create Schema Registry credentials in Confluent Cloud:
In Confluent Cloud, navigate to your environment → Schema Registry → API credentials → + Add key. Copy the Schema Registry API Key and Secret - the secret is shown only once.
Have the user store them as SR_API_KEY and SR_API_SECRET with the same methods as Store the
Confluent credentials above (in the terminal prompt or hand-edited file, use SR_API_KEY= and
SR_API_SECRET= lines in the same ~/.datadog-confluent.env), never by pasting them into the chat. You must collect both together - the API rejects a
POST with only one of the two. If the user skips Schema Registry for now, credentials can be
added later via PATCH (see Phase 4).
Register the account. The name field is optional - the block includes it only if <ACCOUNT_NAME> is
non-empty (if the name contains a single quote, write it as '\'' inside the quotes). Schema
Registry credentials are included only when the user chose Schema Registry above: set SR_OPT_IN to
yes in that case and no otherwise, so leftover SR_API_* values from an earlier setup are never sent.
The block also checks that the loaded Confluent key is the one confirmed above. jq builds the body, so quotes and
backslashes in the name are escaped correctly and the secrets never reach argv:
for f in .env.local .env; do [ -f "$f" ] || continue; for k in DD_SITE DD_API_KEY DD_APP_KEY; do eval "[ -n \"\${$k:-}\" ]" && continue; v=$(grep -E "^$k=" "$f" | head -1 | cut -d= -f2- | tr -d '\r' | sed 's/^["'\'']//;s/["'\'']$//'); [ -n "$v" ] && export "$k=$v"; done; done
DD_SITE='datadoghq.com'; export DD_SITE # <- replace with the site confirmed in Phase 0.
# Explicit assignment, not ':=': a wrong non-empty DD_SITE in .env would otherwise survive.
: "${DD_API_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"; : "${DD_APP_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"
cf="$HOME/.datadog-confluent.env"; if [ -f "$cf" ] && grep -qE '^CONFLUENT_API_KEY=.' "$cf" && grep -qE '^CONFLUENT_API_SECRET=.' "$cf"; then for k in CONFLUENT_API_KEY CONFLUENT_API_SECRET; do export "$k=$(grep -E "^$k=" "$cf" | head -1 | cut -d= -f2- | tr -d '\r')"; done; fi
: "${CONFLUENT_API_KEY:?not set - see Phase 2, Store the Confluent credentials}"; : "${CONFLUENT_API_SECRET:?not set - see Phase 2, Store the Confluent credentials}"
CONFLUENT_KEY_LAST4='<CONFLUENT_API_KEY_LAST4>' # <- the last four characters the user confirmed above
[ "$(printf '%s' "$CONFLUENT_API_KEY" | tail -c 4)" = "$CONFLUENT_KEY_LAST4" ] || { echo "loaded Confluent key: mismatch with the confirmed key - fix the stored credentials first" >&2; exit 1; }
SR_OPT_IN='<yes|no>' # <- yes only if the user chose Schema Registry above
if [ "$SR_OPT_IN" = yes ]; then
cf="$HOME/.datadog-confluent.env"; if [ -f "$cf" ] && grep -qE '^SR_API_KEY=.' "$cf" && grep -qE '^SR_API_SECRET=.' "$cf"; then for k in SR_API_KEY SR_API_SECRET; do export "$k=$(grep -E "^$k=" "$cf" | head -1 | cut -d= -f2- | tr -d '\r')"; done; fi
: "${SR_API_KEY:?not set - store SR_API_KEY and SR_API_SECRET together}"; : "${SR_API_SECRET:?not set - store SR_API_KEY and SR_API_SECRET together}"
else
unset SR_API_KEY SR_API_SECRET
fi
CONFLUENT_ACCOUNT_NAME='<ACCOUNT_NAME>' # <- the display name, or '' to skip
export CONFLUENT_ACCOUNT_NAME
umask 077
body_file=$(mktemp); trap 'rm -f "$body_file"' EXIT
jq -n '{data: {type: "confluent-cloud-accounts", attributes: (
(if $ENV.CONFLUENT_ACCOUNT_NAME != "" then {name: $ENV.CONFLUENT_ACCOUNT_NAME} else {} end)
+ {api_key: $ENV.CONFLUENT_API_KEY, api_secret: $ENV.CONFLUENT_API_SECRET}
+ (if ($ENV.SR_API_KEY // "") != "" then {sr_api_key: $ENV.SR_API_KEY, sr_api_secret: $ENV.SR_API_SECRET} else {} end)
)}}' > "$body_file"
resp=$(printf 'DD-API-KEY: %s\nDD-APPLICATION-KEY: %s\n' "$DD_API_KEY" "$DD_APP_KEY" \
| curl -H @- -sS -w '\n%{http_code}' -X POST "https://api.${DD_SITE}/api/v2/integrations/confluent-cloud/accounts" \
-H "Content-Type: application/json" \
--data-binary @"$body_file")
code=$(printf '%s' "$resp" | tail -1); body=$(printf '%s' "$resp" | sed '$d')
echo "HTTP $code"
if [ "$code" = "201" ] || [ "$code" = "200" ]; then
echo "CONFLUENT_ACCOUNT_ID=$(printf '%s' "$body" | jq -r '.data.id')"
else
printf '%s\n' "$body"
fiOn success, the printed CONFLUENT_ACCOUNT_ID is the value to substitute for <CONFLUENT_ACCOUNT_ID>
in every later block.
Error handling:
409 Conflict - an account with this API key already exists. Use GET /accounts to find its
ID and proceed to Phase 3 with the existing account.400 / 422 - the API key or secret is invalid. Ask the user to verify what they copied from
Confluent Cloud. The most common mistake is selecting a cluster-scoped key instead of a
cloud-resource-management key.Confluent Cloud resources are individual objects Datadog monitors: Kafka clusters, connectors, ksqlDB clusters, Schema Registry instances, and Flink compute pools. Each has a Confluent-assigned resource ID.
This step requires CONFLUENT_API_KEY and CONFLUENT_API_SECRET to be stored - either from
Phase 2 (new account) or collected from the user in Phase 1 (existing account). Each block below
reloads them from ~/.datadog-confluent.env (or the parent-shell environment). If the
credentials are not available, skip directly to Manual Resource Entry below.
Use the Confluent Cloud REST API to enumerate available resources. The Cloud resource management API key can list environments, clusters, and connectors.
Step 1 - List environments:
cf="$HOME/.datadog-confluent.env"; if [ -f "$cf" ] && grep -qE '^CONFLUENT_API_KEY=.' "$cf" && grep -qE '^CONFLUENT_API_SECRET=.' "$cf"; then for k in CONFLUENT_API_KEY CONFLUENT_API_SECRET; do export "$k=$(grep -E "^$k=" "$cf" | head -1 | cut -d= -f2- | tr -d '\r')"; done; fi
: "${CONFLUENT_API_KEY:?not set - see Phase 2, Store the Confluent credentials}"; : "${CONFLUENT_API_SECRET:?not set - see Phase 2, Store the Confluent credentials}"
printf 'user = "%s:%s"\n' "$CONFLUENT_API_KEY" "$CONFLUENT_API_SECRET" |
curl -s -K - \
"https://api.confluent.cloud/org/v2/environments"Extract each environment’s id (e.g. env-abc123) and display_name. If the response contains
a metadata.next link, paginate by following that URL until all environments are retrieved.
Step 2 - For each environment, list Kafka clusters:
cf="$HOME/.datadog-confluent.env"; if [ -f "$cf" ] && grep -qE '^CONFLUENT_API_KEY=.' "$cf" && grep -qE '^CONFLUENT_API_SECRET=.' "$cf"; then for k in CONFLUENT_API_KEY CONFLUENT_API_SECRET; do export "$k=$(grep -E "^$k=" "$cf" | head -1 | cut -d= -f2- | tr -d '\r')"; done; fi
: "${CONFLUENT_API_KEY:?not set - see Phase 2, Store the Confluent credentials}"; : "${CONFLUENT_API_SECRET:?not set - see Phase 2, Store the Confluent credentials}"
printf 'user = "%s:%s"\n' "$CONFLUENT_API_KEY" "$CONFLUENT_API_SECRET" |
curl -s -K - \
"https://api.confluent.cloud/cmk/v2/clusters?environment=<ENV_ID>"Extract each cluster’s id (starts with lkc-), spec.display_name, and
spec.availability / spec.cloud / spec.region for context. Paginate if needed.
Step 3 - For each environment + cluster, list connectors (best-effort):
cf="$HOME/.datadog-confluent.env"; if [ -f "$cf" ] && grep -qE '^CONFLUENT_API_KEY=.' "$cf" && grep -qE '^CONFLUENT_API_SECRET=.' "$cf"; then for k in CONFLUENT_API_KEY CONFLUENT_API_SECRET; do export "$k=$(grep -E "^$k=" "$cf" | head -1 | cut -d= -f2- | tr -d '\r')"; done; fi
: "${CONFLUENT_API_KEY:?not set - see Phase 2, Store the Confluent credentials}"; : "${CONFLUENT_API_SECRET:?not set - see Phase 2, Store the Confluent credentials}"
printf 'user = "%s:%s"\n' "$CONFLUENT_API_KEY" "$CONFLUENT_API_SECRET" |
curl -s -K - \
"https://api.confluent.cloud/connect/v1/environments/<ENV_ID>/clusters/<CLUSTER_ID>/connectors?expand=info,status,id"Important: the ?expand=info,status,id query parameter is required - without it the API
returns only a plain array of connector names with no IDs. With the expand parameter, the
response is a JSON object keyed by connector name, where each value contains an id.id field
with the lcc- prefixed connector ID. Extract the ID from connector["id"]["id"] and the
name from the object key.
This may return 403 or 401 if the API key scope doesn’t cover connectors - that’s fine,
skip connectors and note it to the user.
Step 4 - Compile the inventory and ask about undiscovered resource types:
Build a table of all discovered resources. Only include rows for resources that were found:
Here’s what I found in your Confluent Cloud organization:
# Type Name ID Environment 1 Kafka Cluster prod-cluster lkc-abc123 Production (env-xyz) 2 Kafka Cluster staging-cluster lkc-def456 Staging (env-uvw) 3 Connector s3-sink lcc-ghi789 Production (env-xyz) Which resources would you like Datadog to monitor? You can say “all”, list numbers, or name specific ones.
After the user selects from the discovered resources, also ask about resource types that automatic discovery does not cover:
Automatic discovery found Kafka clusters and connectors. Datadog can also monitor ksqlDB clusters and Schema Registry instances if you have them. Do you have any ksqlDB or Schema Registry resources you’d like to add?
If the user says yes, collect those resource IDs using the Manual Resource Entry prompts
below for just those types (ksqlDB IDs start with lksqlc-, Schema Registry IDs with lsrc-).
If the Confluent API calls fail entirely (e.g. authentication error), fall back to Manual Resource Entry (below) for all resource types.
If automatic discovery is not available, ask the user to provide resource IDs from the Confluent Cloud console:
lkc-lcc-lksqlc-lsrc-Collect each resource ID and its type from the user.
For each resource the user selected, register it with Datadog:
for f in .env.local .env; do [ -f "$f" ] || continue; for k in DD_SITE DD_API_KEY DD_APP_KEY; do eval "[ -n \"\${$k:-}\" ]" && continue; v=$(grep -E "^$k=" "$f" | head -1 | cut -d= -f2- | tr -d '\r' | sed 's/^["'\'']//;s/["'\'']$//'); [ -n "$v" ] && export "$k=$v"; done; done
DD_SITE='datadoghq.com'; export DD_SITE # <- replace with the site confirmed in Phase 0.
# Explicit assignment, not ':=': a wrong non-empty DD_SITE in .env would otherwise survive.
: "${DD_API_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"; : "${DD_APP_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"
CONFLUENT_ACCOUNT_ID='<CONFLUENT_ACCOUNT_ID>' # <- the account id from Phase 1 or Phase 2
: "${CONFLUENT_ACCOUNT_ID:?not set - substitute the Confluent account id from Phase 1 or Phase 2}"
body=$(jq -n --arg id '<RESOURCE_ID>' --arg type '<RESOURCE_TYPE>' --argjson custom false \
'{data: {type: "confluent-cloud-resources", id: $id, attributes: {resource_type: $type, enable_custom_metrics: $custom}}}')
resp=$(printf 'DD-API-KEY: %s\nDD-APPLICATION-KEY: %s\n' "$DD_API_KEY" "$DD_APP_KEY" \
| curl -H @- -sS -w '\n%{http_code}' -X POST "https://api.${DD_SITE}/api/v2/integrations/confluent-cloud/accounts/${CONFLUENT_ACCOUNT_ID}/resources" \
-H "Content-Type: application/json" \
--data-binary "$body")
code=$(printf '%s' "$resp" | tail -1)
echo "HTTP $code"; printf '%s' "$resp" | sed '$d'; echoReplace <RESOURCE_ID> and <RESOURCE_TYPE> with:
| Resource | resource_type |
|---|---|
| Kafka cluster | kafka |
| Connector | connector |
| ksqlDB | ksql |
| Schema Registry | schema_registry |
For Kafka clusters, offer to enable enhanced consumer lag metrics:
Would you like to enable the
custom.consumer_lag_offsetmetric for this cluster? It provides per-partition lag detail but may increase custom metric volume.
If the user says yes, use --argjson custom true.
Confirm each resource registers successfully (HTTP 201). If a resource fails with 409 Conflict,
it’s already registered, and the POST did not apply the requested enable_custom_metrics. For a
Kafka cluster where the user asked for enhanced consumer lag metrics, read the existing resource and
turn the flag on if it is off (confirm with the user first; it can raise custom metric volume):
for f in .env.local .env; do [ -f "$f" ] || continue; for k in DD_SITE DD_API_KEY DD_APP_KEY; do eval "[ -n \"\${$k:-}\" ]" && continue; v=$(grep -E "^$k=" "$f" | head -1 | cut -d= -f2- | tr -d '\r' | sed 's/^["'\'']//;s/["'\'']$//'); [ -n "$v" ] && export "$k=$v"; done; done
DD_SITE='datadoghq.com'; export DD_SITE # <- replace with the site confirmed in Phase 0.
# Explicit assignment, not ':=': a wrong non-empty DD_SITE in .env would otherwise survive.
: "${DD_API_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"; : "${DD_APP_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"
CONFLUENT_ACCOUNT_ID='<CONFLUENT_ACCOUNT_ID>' # <- the account id from Phase 1 or Phase 2
RESOURCE_ID='<RESOURCE_ID>' # <- the lkc- cluster id that returned 409
: "${CONFLUENT_ACCOUNT_ID:?}"; : "${RESOURCE_ID:?}"
url="https://api.${DD_SITE}/api/v2/integrations/confluent-cloud/accounts/${CONFLUENT_ACCOUNT_ID}/resources/${RESOURCE_ID}"
dd_headers() { printf 'DD-API-KEY: %s\nDD-APPLICATION-KEY: %s\n' "$DD_API_KEY" "$DD_APP_KEY"; }
current=$(dd_headers | curl -H @- -fsS "$url" | jq -er '.data.attributes.enable_custom_metrics | if type == "boolean" then tostring else error("expected boolean") end') \
|| { echo "could not read resource ${RESOURCE_ID}" >&2; exit 1; }
echo "enable_custom_metrics currently: $current"
if [ "$current" != true ]; then
body=$(jq -n --arg id "$RESOURCE_ID" '{data: {type: "confluent-cloud-resources", id: $id, attributes: {resource_type: "kafka", enable_custom_metrics: true}}}')
resp=$(dd_headers | curl -H @- -sS -w '\n%{http_code}' -X PATCH "$url" -H "Content-Type: application/json" --data-binary "$body")
code=$(printf '%s' "$resp" | tail -1)
echo "HTTP $code"; [ "$code" = "200" ] || printf '%s\n' "$(printf '%s' "$resp" | sed '$d')"
fiIf the PATCH does not return 200, tell the user the enhanced metrics setting was not applied
rather than reporting it as enabled. For any other already-registered resource, inform the user and
continue. Any other status is a failure: show the body and ask the user how to proceed.
Skip this section if an account name was already set during registration in Phase 2.
Ask: “Would you like to set or update the display name for this account?”
If yes, collect the name and PATCH the account. The block reads the account’s current api_key (the
key id, not the secret) from Datadog rather than from the session, so it also works for an existing
account whose credentials were never loaded, and jq escapes the name:
for f in .env.local .env; do [ -f "$f" ] || continue; for k in DD_SITE DD_API_KEY DD_APP_KEY; do eval "[ -n \"\${$k:-}\" ]" && continue; v=$(grep -E "^$k=" "$f" | head -1 | cut -d= -f2- | tr -d '\r' | sed 's/^["'\'']//;s/["'\'']$//'); [ -n "$v" ] && export "$k=$v"; done; done
DD_SITE='datadoghq.com'; export DD_SITE # <- replace with the site confirmed in Phase 0.
# Explicit assignment, not ':=': a wrong non-empty DD_SITE in .env would otherwise survive.
: "${DD_API_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"; : "${DD_APP_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"
CONFLUENT_ACCOUNT_ID='<CONFLUENT_ACCOUNT_ID>' # <- the account id from Phase 1 or Phase 2
: "${CONFLUENT_ACCOUNT_ID:?not set - substitute the Confluent account id from Phase 1 or Phase 2}"
CONFLUENT_ACCOUNT_NAME='<ACCOUNT_NAME>' # <- the new display name
: "${CONFLUENT_ACCOUNT_NAME:?not set - substitute the display name}"
dd_headers() { printf 'DD-API-KEY: %s\nDD-APPLICATION-KEY: %s\n' "$DD_API_KEY" "$DD_APP_KEY"; }
current_key=$(dd_headers | curl -H @- -fsS "https://api.${DD_SITE}/api/v2/integrations/confluent-cloud/accounts/${CONFLUENT_ACCOUNT_ID}" \
| jq -er '.data.attributes.api_key') || { echo "could not read account ${CONFLUENT_ACCOUNT_ID}" >&2; exit 1; }
export current_key
umask 077
body_file=$(mktemp); trap 'rm -f "$body_file"' EXIT
jq -n --arg id "$CONFLUENT_ACCOUNT_ID" --arg name "$CONFLUENT_ACCOUNT_NAME" \
'{data: {type: "confluent-cloud-accounts", id: $id, attributes: {name: $name, api_key: $ENV.current_key}}}' > "$body_file"
resp=$(dd_headers | curl -H @- -sS -w '\n%{http_code}' -X PATCH "https://api.${DD_SITE}/api/v2/integrations/confluent-cloud/accounts/${CONFLUENT_ACCOUNT_ID}" \
-H "Content-Type: application/json" \
--data-binary @"$body_file")
code=$(printf '%s' "$resp" | tail -1)
echo "HTTP $code"; [ "$code" = "200" ] || printf '%s\n' "$(printf '%s' "$resp" | sed '$d')"Confirm the PATCH returns HTTP 200.
Skip this section if Schema Registry credentials were already configured during account registration in Phase 2.
Ask: “Do you want Datadog to collect tags defined in Confluent Cloud’s Schema Registry?”
If yes, guide the user to create Schema Registry credentials in Confluent Cloud if they don’t have them yet:
In Confluent Cloud, navigate to your environment → Schema Registry → API credentials → + Add key. Copy the Schema Registry API Key and Secret - the secret is shown only once.
Have the user store SR_API_KEY and SR_API_SECRET as in Phase 2 (terminal prompt, hand-edited
~/.datadog-confluent.env, or parent-shell export - never pasted into the chat), then PATCH the account.
Like the rename PATCH, the block reads the account’s current api_key (the key id, not the secret) from
Datadog and sends it along, since the account-update schema lists api_key as required:
for f in .env.local .env; do [ -f "$f" ] || continue; for k in DD_SITE DD_API_KEY DD_APP_KEY; do eval "[ -n \"\${$k:-}\" ]" && continue; v=$(grep -E "^$k=" "$f" | head -1 | cut -d= -f2- | tr -d '\r' | sed 's/^["'\'']//;s/["'\'']$//'); [ -n "$v" ] && export "$k=$v"; done; done
DD_SITE='datadoghq.com'; export DD_SITE # <- replace with the site confirmed in Phase 0.
# Explicit assignment, not ':=': a wrong non-empty DD_SITE in .env would otherwise survive.
: "${DD_API_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"; : "${DD_APP_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"
CONFLUENT_ACCOUNT_ID='<CONFLUENT_ACCOUNT_ID>' # <- the account id from Phase 1 or Phase 2
: "${CONFLUENT_ACCOUNT_ID:?not set - substitute the Confluent account id from Phase 1 or Phase 2}"
cf="$HOME/.datadog-confluent.env"; if [ -f "$cf" ] && grep -qE '^SR_API_KEY=.' "$cf" && grep -qE '^SR_API_SECRET=.' "$cf"; then for k in SR_API_KEY SR_API_SECRET; do export "$k=$(grep -E "^$k=" "$cf" | head -1 | cut -d= -f2- | tr -d '\r')"; done; fi
: "${SR_API_KEY:?not set - store SR_API_KEY as in Phase 2}"; : "${SR_API_SECRET:?not set - store SR_API_SECRET as in Phase 2}"
current_key=$(printf 'DD-API-KEY: %s\nDD-APPLICATION-KEY: %s\n' "$DD_API_KEY" "$DD_APP_KEY" \
| curl -H @- -fsS "https://api.${DD_SITE}/api/v2/integrations/confluent-cloud/accounts/${CONFLUENT_ACCOUNT_ID}" \
| jq -er '.data.attributes.api_key') || { echo "could not read account ${CONFLUENT_ACCOUNT_ID}" >&2; exit 1; }
export CONFLUENT_ACCOUNT_ID current_key
umask 077
body_file=$(mktemp); trap 'rm -f "$body_file"' EXIT
jq -n '{data: {type: "confluent-cloud-accounts", id: $ENV.CONFLUENT_ACCOUNT_ID,
attributes: {api_key: $ENV.current_key, sr_api_key: $ENV.SR_API_KEY, sr_api_secret: $ENV.SR_API_SECRET}}}' > "$body_file"
resp=$(printf 'DD-API-KEY: %s\nDD-APPLICATION-KEY: %s\n' "$DD_API_KEY" "$DD_APP_KEY" \
| curl -H @- -sS -w '\n%{http_code}' -X PATCH "https://api.${DD_SITE}/api/v2/integrations/confluent-cloud/accounts/${CONFLUENT_ACCOUNT_ID}" \
-H "Content-Type: application/json" \
--data-binary @"$body_file")
code=$(printf '%s' "$resp" | tail -1)
echo "HTTP $code"; [ "$code" = "200" ] || printf '%s\n' "$(printf '%s' "$resp" | sed '$d')"PATCH merge rules - the API is round-trip safe:
sr_api_key + sr_api_secret provided → replaces any existing SR credentials.sr_api_secret (no sr_api_key) → rotates the secret against the existing SR key.
If there is no existing SR key, the request returns 400 AccountAPISecretRequired.sr_api_key with a different key value → returns 400 AccountAPISecretRequired
(the new key requires a matching secret).Confirm the PATCH returns HTTP 200. Schema Registry tags will appear on metrics shortly.
Ask: “Do you also want Confluent Cloud cost data in Cloud Cost Management?”
If yes:
<APP_BASE>/integrations/confluent-cloud,
select this account, and under Resources turn on Collect cost data to view in Cloud Cost
Management, then click Save. The BillingAdmin role only authorizes access to Confluent’s billing
API; without this toggle Datadog collects no cost data. Ask the user to confirm it is on before
reporting Cloud Cost Management as configured.Inform the user:
<APP_BASE>/costThe 201 responses in Phase 3 confirm the integration is configured; this step checks that
telemetry is actually observed for the resources you registered. Wait a moment for metrics to
begin flowing, then query one metric per registered resource, scoped to its resource_id, so another
Confluent integration in the same org can’t produce a false positive. Set RESOURCES to the registered
resources as <RESOURCE_TYPE>:<RESOURCE_ID> pairs:
for f in .env.local .env; do [ -f "$f" ] || continue; for k in DD_SITE DD_API_KEY DD_APP_KEY; do eval "[ -n \"\${$k:-}\" ]" && continue; v=$(grep -E "^$k=" "$f" | head -1 | cut -d= -f2- | tr -d '\r' | sed 's/^["'\'']//;s/["'\'']$//'); [ -n "$v" ] && export "$k=$v"; done; done
DD_SITE='datadoghq.com'; export DD_SITE # <- replace with the site confirmed in Phase 0.
# Explicit assignment, not ':=': a wrong non-empty DD_SITE in .env would otherwise survive.
: "${DD_API_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"; : "${DD_APP_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"
RESOURCES='<RESOURCE_TYPE>:<RESOURCE_ID> <RESOURCE_TYPE>:<RESOURCE_ID>' # <- e.g. 'kafka:lkc-abc123 connector:lcc-ghi789'
for r in $RESOURCES; do
type=${r%%:*}; id=${r#*:}
case "$type" in
kafka) metrics=confluent_cloud.kafka.received_bytes ;;
# Source connectors report sent_records, sink connectors received_records; accept either.
connector) metrics="confluent_cloud.connect.sent_records confluent_cloud.connect.received_records" ;;
ksql) metrics=confluent_cloud.ksql.streaming_unit_count ;;
schema_registry) metrics=confluent_cloud.schema_registry.schema_count ;;
*) echo "$r: unknown resource type"; continue ;;
esac
for metric in $metrics; do
resp=$(printf 'DD-API-KEY: %s\nDD-APPLICATION-KEY: %s\n' "$DD_API_KEY" "$DD_APP_KEY" \
| curl -H @- -sS -w '\n%{http_code}' -G "https://api.${DD_SITE}/api/v1/query" \
--data-urlencode "from=$(($(date +%s) - 900))" \
--data-urlencode "to=$(date +%s)" \
--data-urlencode "query=avg:${metric}{resource_id:${id}}")
code=$(printf '%s' "$resp" | tail -1)
[ "$code" = "200" ] || { echo "$r ($metric): query failed with HTTP $code"; continue; }
echo "$r ($metric): $(printf '%s' "$resp" | sed '$d' | jq '.series | length') series"
done
doneIf a resource has a non-empty series, metrics are flowing for it. Congratulate the user.
If a resource has no series yet, let the user know metrics are still propagating - Confluent Cloud metrics typically arrive within a few minutes, and an idle connector or ksqlDB cluster may report little. Suggest checking back shortly, and report that resource as configured but not yet observed.
After the verify step, post a completion status to the onboarding workflow API so the UI can
mark the agentic flow complete. DD_WORKFLOW_ID is the UUID of the Datadog user the credentials
belong to, which is how the UI correlates this callback. Resolve it and post in one command: many
agent runtimes start a fresh shell per command, so a variable assigned in an earlier one would be
empty here.
Never print the current_user response: it also carries the user’s name, handle, and email. If no
JSON parser is available, skip this callback rather than installing one or blocking setup.
for f in .env.local .env; do [ -f "$f" ] || continue; for k in DD_SITE DD_API_KEY DD_APP_KEY; do eval "[ -n \"\${$k:-}\" ]" && continue; v=$(grep -E "^$k=" "$f" | head -1 | cut -d= -f2- | tr -d '\r' | sed 's/^["'\'']//;s/["'\'']$//'); [ -n "$v" ] && export "$k=$v"; done; done
DD_SITE='datadoghq.com'; export DD_SITE # <- replace with the site confirmed in Phase 0.
# Explicit assignment, not ':=': a wrong non-empty DD_SITE in .env would otherwise survive.
: "${DD_API_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"; : "${DD_APP_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"
CONFLUENT_ACCOUNT_ID='<CONFLUENT_ACCOUNT_ID>' # <- the account id from Phase 1 or Phase 2
: "${CONFLUENT_ACCOUNT_ID:?not set - substitute the Confluent account id from Phase 1 or Phase 2}"
dd_headers() { printf 'DD-API-KEY: %s\nDD-APPLICATION-KEY: %s\n' "$DD_API_KEY" "$DD_APP_KEY"; }
DD_WORKFLOW_ID="$(dd_headers | curl -H @- -fsS --connect-timeout 5 --max-time 20 \
"https://api.${DD_SITE}/api/v2/current_user" | jq -er '.data.id')" \
&& dd_headers | curl -H @- -sS --connect-timeout 5 --max-time 20 -X POST \
"https://api.${DD_SITE}/api/unstable/integration/confluent/workflow/agentic_saas_integration_setup" \
-H "Content-Type: application/vnd.api+json" \
-d '{
"data": {
"type": "integration_setup_status",
"id": "'"${DD_WORKFLOW_ID}"'",
"attributes": {
"status": "finished",
"step": "setup_complete",
"metadata": {
"confluent_account_id": "'"${CONFLUENT_ACCOUNT_ID}"'"
}
}
}
}'Do not retry or block setup if this callback fails.
Give the user a short summary of what was set up:
CONFLUENT_ACCOUNT_ID, and its display name if set) and
whether it was newly registered or already existed.409) and any skipped
(for example connectors when the key could not list them), and which Kafka clusters have
enable_custom_metrics on.Once the account is registered, Datadog holds the credentials, so ~/.datadog-confluent.env is no
longer needed. Offer to delete it (rm -f ~/.datadog-confluent.env); keep it only if the user plans to
re-run resource discovery soon.
Then provide these links (construct as <APP_BASE>/..., from the Phase 0 App URL table):
<APP_BASE>/integrations/confluent-cloud -
verify the integration is active and view the out-of-the-box dashboard.<APP_BASE>/metric/explorer?exp_metric=confluent_cloud.kafka.received_bytes -
confirm Kafka metrics are flowing.<APP_BASE>/cost - Confluent spend appears
within ~24 hours.Recommended next steps:
<APP_BASE>/monitors/create using
confluent_cloud.kafka.consumer_lag_offsets to alert on processing backlogs.confluent_cloud.kafka.cluster_load_percent to track cluster utilization.enable_custom_metrics on Kafka clusters enables custom.consumer_lag_offset with extra
per-partition tags; it may increase custom metric volume.sr_api_key, sr_api_secret) can be set at account creation
(POST) or added later (PATCH). POST requires both fields together or neither; PATCH supports
secret-only rotation but requires the existing key to be present.CONFLUENT_ACCOUNT_ID from GET /accounts.Set up the Datadog Confluent Cloud integration - create a Cloud resource management API key in Confluent Cloud (with the Confluent CLI or the console), register the account with Datadog, discover and register its Kafka clusters, connectors, ksqlDB and Schema Registry resources, and optionally add Schema Registry tags and Cloud Cost Management. Use when the user wants to monitor Confluent Cloud Kafka clusters, set up the Datadog Confluent Cloud integration, connect Confluent Cloud to Datadog, see Confluent metrics in Datadog, or monitor Kafka clusters on Confluent. Does not set up self-managed Kafka or the Datadog Agent. Requires a Datadog API key and application key; run `dd-account-setup` first if they are missing.
The verbatim description from this skill’s front matter — the string an agent matches on to decide whether to load it.
main, last pushed 6 November 2026.SKILL.md, not by matching a directory convention. 11 distinct layouts observed: agent-observability/*/SKILL.md, */SKILL.md, dd-android-sdk/*/SKILL.md, dd-apm/*/SKILL.md, dd-apm/k8s-ssi/*/SKILL.md, dd-apm/linux-ssi/*/SKILL.md, dd-apps/*/SKILL.md, dd-audit/*/SKILL.md, dd-browser-sdk/*/SKILL.md, dd-security/csm/*/SKILL.md, dd-software-delivery/*/SKILL.md.h1 and no skipped levels:.claude-plugin/marketplace.json by datadog-labs, declaring 1 plugin. It is read for editorial metadata only — never as the skill index, which is always the repository tree./datadog-labs/agent-skills.md, and each skill at its own .md URL.