Dynatrace
Dynatrace delivers to BatonDeck through a problem notification with a custom payload.
- Auth:
api_key— Dynatrace cannot compute the BatonDeck HMAC, but it can send a custom header. - Endpoint:
POST https://<your-oncall-host>/i/<integrationId> - Header:
x-batondeck-key: <your key> - One problem notification = one incident ticket.
1. Create the integration
create_integration {
projectId, name: "prod-dynatrace", source: "dynatrace", boardId: "B-…",
auth: { mode: "api_key", apiKey: "<a long random value you generate>" },
routing: { mode: "oncall", onCallAgent: "claude-oncall" },
mapping: {
severityMap: {
availability: "urgent",
error: "urgent",
performance: "high",
resource_contention: "high",
monitoring_unavailable: "high",
custom_alert: "normal"
}
}
}Set severityMap. Dynatrace's {ProblemSeverity} renders its own vocabulary —
AVAILABILITY, PERFORMANCE, RESOURCE_CONTENTION, CUSTOM_ALERT,
MONITORING_UNAVAILABLE — and only ERROR overlaps with BatonDeck's built-in severity words.
Everything else falls through to priority normal, so a total availability outage arrives at the
same priority as a warning unless you map it.
2. Add the problem notification
Settings → Integration → Problem notifications → Set up notifications → Custom integration.
| Field | Value |
|---|---|
| Webhook URL | https://<your-oncall-host>/i/ing_0123456789abcdef0123456789abcdef |
| Additional HTTP headers | name x-batondeck-key, value your key (mark it a secret) |
| Custom payload | the template below |
| Call webhook if new events merge into existing problems | your choice — see Lifecycle below |
3. Custom payload template
Paste this verbatim. Every field name is one BatonDeck reads.
{
"problemId": "{PID}",
"state": "{State}",
"problemTitle": "{ProblemTitle}",
"severityLevel": "{ProblemSeverity}",
"problemImpact": "{ProblemImpact}",
"problemUrl": "{ProblemURL}",
"problemDetailsText": "{ProblemDetailsText}",
"impactedEntities": {ImpactedEntities}
}The one thing to get right:
{ImpactedEntities}is NOT quoted. It renders a JSON array, and BatonDeck reads it only when it arrives as an array. Wrap it in quotes and it becomes a string — which is not an error, produces no warning, and silently drops every impacted entity name from the ticket. Every other placeholder in the template is quoted, which is exactly what makes this one easy to miss.
BatonDeck also accepts Dynatrace's default placeholder names case-insensitively — PID, State,
ProblemTitle, ProblemSeverity, ProblemURL, ProblemDetailsText, ProblemImpact,
ImpactedEntities — so an existing template built from those names works without renaming anything.
problemId is required. A payload without {PID} (or ProblemID) is refused with
400 unrecognized_payload, because it is the correlation key: without it, every notification about
one problem would open a new ticket.
No timestamp field. The template sends none, so occurredAt is the moment BatonDeck receives
the delivery — within seconds of the notification for a problem notification. If you add one, it
must be an ISO 8601 string; an epoch-millisecond value falls back to receipt time silently.
4. What lands on the ticket
| Ticket field | Comes from |
|---|---|
| Correlation key | problemId / PID |
| Firing / resolved | state — RESOLVED or CLOSED resolves; anything else (including OPEN) fires |
| Severity | severityLevel / ProblemSeverity, lowercased; defaults to warning |
| Title | problemTitle, else "Dynatrace problem" |
| Description | problemDetailsText |
| Links | problemUrl |
Annotation dynatrace_state | the uppercased state |
Annotation dynatrace_impact | problemImpact |
Annotation impacted_entities | the name of each entry in impactedEntities, comma-joined |
Lifecycle
With the default correlate lifecycle, repeat notifications about the same problemId bump the
occurrence count on the open ticket, and a RESOLVED notification annotates it and leaves it open
for the agent to close. Switch to autoclose if you want an untouched ticket cancelled when
Dynatrace closes the problem — it will never yank a ticket an agent has already claimed.
When nothing arrives
| Delivery log reason | What it means |
|---|---|
| No rows at all | Dynatrace never reached the function — use the notification's Send test notification button, which posts a real request through the whole path. |
api_key_mismatch | The header name or value differs. api_key mode reads only x-batondeck-key; an Authorization header is consulted only in jwt mode. |
400 unrecognized_payload | The detail names the problem. Missing problemId/PID is the common one. |
400 invalid_json | Usually {ProblemDetailsText} rendering multi-line text inside a JSON string. Drop that line, re-test, then decide whether you need it. |
404 from the endpoint | Wrong integration id, or the integration is disabled. Both answer identically on purpose. |
Next: On-call ingestion overview · Datadog
