Docs · Integrations
Connect your stack
Ketl reads alerts, logs, deploys, and past incidents. The more signal it receives at the moment an alert fires, the sharper the read. This guide covers forwarding alerts via webhook and what each native connector pulls on Team.
Overview
All connections are read-only. Ketl never writes to your monitoring tools, never acknowledges an alert on your behalf, and never modifies a deploy. It reads signal, reasons about cause, and returns a diagnosis.
When an alert fires, Ketl queries the surrounding window: typically the 30 minutes before and 5 minutes after the alert timestamp. It pulls logs, any deploys in that window, and the most recent matching past incidents. All of this reaches the reasoning engine at once.
Free tier: copy and paste telemetry into the dashboard or call POST /api/diagnose from your own code (see the quickstart). The webhook handler in the next section works on any tier. Team: native connectors run automatically, no copy-paste required.
Forward an alert via webhook
Any alert provider that can send an outbound HTTP webhook can trigger Ketl. The pattern: your webhook handler receives the alert, fetches the relevant log lines from your log aggregator, and calls POST /api/diagnose.
Set KETL_API_URL in your deployment environment. On the free tier, also set KETL_SESSION_COOKIE to the value of your next-auth.session-token cookie (copy it from browser devtools after signing in). On Team, replace the Cookie header with X-Ketl-Token: <your-api-token>.
// ketl-forward.ts: minimal webhook handler (Node 20+, any runtime)
// Drop this into a Next.js route, an Express endpoint, or a Lambda function.
interface KetlPayload {
service?: string;
alert: string; // min 8 chars
logs: string; // min 20 chars; raw log lines joined with \n
deploys?: string;
incidents?: string;
}
interface KetlResponse {
result: {
summary: string;
severity: "critical" | "warning" | "info";
rootCauses: { rank: number; title: string; confidence: number }[];
proposedFix: {
kind: "fix" | "rollback" | "mitigation";
title: string;
command: string | null;
};
};
usage: { used: number; limit: number; plan: string };
}
const KETL_URL =
process.env.KETL_API_URL ?? "https://getketl.com/api/diagnose";
export async function forwardToKetl(
payload: KetlPayload,
): Promise<KetlResponse> {
const res = await fetch(KETL_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
// Free tier: session cookie copied from browser devtools.
// Team: "X-Ketl-Token": process.env.KETL_API_TOKEN ?? ""
Cookie: process.env.KETL_SESSION_COOKIE ?? "",
},
body: JSON.stringify(payload),
});
if (res.status === 402) {
throw new Error("Monthly diagnosis limit reached. Upgrade at /pricing.");
}
if (!res.ok) {
throw new Error("Ketl returned HTTP " + String(res.status));
}
return res.json() as Promise<KetlResponse>;
}
// Example: called from a PagerDuty outbound webhook handler.
// body.alert comes from the PD event summary; body.logs comes from
// your log aggregator (Datadog, CloudWatch, etc.) fetched in parallel.
async function handlePagerDutyWebhook(body: {
alert: string;
service: string;
logs: string;
deploys?: string;
}) {
const diagnosis = await forwardToKetl({
service: body.service,
alert: body.alert,
logs: body.logs,
deploys: body.deploys,
});
const fix = diagnosis.result.proposedFix;
console.log(diagnosis.result.severity, fix.title, fix.command);
// Route the result to Slack, PD notes, or your on-call tool of choice.
}The same call with curl for quick testing:
# Replace the Cookie value with your session token from browser devtools.
curl -s -X POST "https://getketl.com/api/diagnose" \
-H "Content-Type: application/json" \
-H "Cookie: next-auth.session-token=<your-token>" \
-d '{
"service": "payment-service",
"alert": "P2: payment-service p99 latency 4200ms (threshold 800ms)",
"logs": "2026-07-22T09:01:14Z WARN payment-service: db pool exhausted (60/60)\n2026-07-22T09:01:15Z ERROR payment-service: context deadline exceeded\n2026-07-22T09:01:16Z ERROR payment-service: context deadline exceeded",
"deploys": "2026-07-22T08:44:00Z payment-service v3.1.2 by ci-runner"
}' | jq '{severity: .result.severity, cause: .result.rootCauses[0].title, command: .result.proposedFix.command}'Correlate GitHub deploys
When Ketl knows about your recent releases it can match a deploy to the error spike with high confidence. On Team, point Ketl at your repository and it listens for deployment_status events. On the free tier, paste your deploy log into the Recent deploys field in the dashboard or include it in the deploys field of the API request.
The deploy text does not need to be structured. Ketl reads lines like 2026-07-22T08:44:00Z payment-service v3.1.2 by ci-runner or the default GitHub deployment status message. One line per release is sufficient; a 30-minute lookback window is enough for most incidents.
On Team, configure the connector in the integrations dashboard. The config looks like:
# .ketl/config.yaml (Team plan - configure in the integrations dashboard)
deploys:
github:
repo: your-org/your-service
events:
- deployment_status # fires when a deployment succeeds or fails
lookback: 30m # window before the alert to search for deploys
environments:
- production
- stagingPagerDuty, Datadog, Grafana, and Sentry
On Team, native connectors let Ketl pull the surrounding signal automatically when an alert fires. All connectors are read-only and require a scoped credential with the minimum permissions listed below.
PagerDuty
Ketl reads the incident title, service name, urgency, and any log summaries or custom details attached to the PagerDuty event. It does not read notification settings, escalation policies, or user data. Connect with a read-only REST API key (Events v2 is not required; a standard read key on the Incidents and Services scopes is sufficient). When PagerDuty fires, Ketl fetches the open incident and runs the diagnosis automatically.
Datadog
Ketl queries Datadog Logs for the time window around the alert, then fetches any associated monitor event for context. It reads logs and monitor results only; it does not read dashboards, users, or billing data. Supply a read-only API key scoped to logs:read and monitors:read. Ketl queries one service at a time and does not store raw log content beyond the request lifetime.
Grafana
Ketl reads alert annotations and the datasource query backing the alerting panel. It does not modify dashboards, silence alerts, or write to any datasource. Create a Grafana service account with the Viewer role and generate a token for it. Ketl uses the token to call the Grafana HTTP API for the alert annotation and the surrounding panel data window.
Sentry
Ketl reads the exception event, stack trace, breadcrumbs, and any attached log messages from Sentry. It does not read team membership, notification rules, or billing. Generate an internal integration token in Sentry with event:read and project:read scopes. Ketl matches the Sentry event to the alert window and includes the stack trace in the reasoning context.
Free tier vs Team
On the free tier, all of the above is available through copy-and-paste into the dashboard or via the POST /api/diagnose API. The webhook handler shown above works today without a Team subscription; you just supply your session cookie manually.
On Team, native connectors fire automatically the moment an alert does. Ketl fetches logs, deploys, and past incidents from your connected sources, runs the diagnosis, and delivers the result to your configured output (Slack, PagerDuty notes, or a webhook). No on-call engineer has to paste anything at 3am.
Compare tiers on the pricing page, or start with the quickstart to run your first diagnosis now.