End-to-End SaaS Observability Demo
This demo sends a deterministic set of synthetic SaaS workflow events to Telemetry and queries them back with SQL. It is intentionally small enough to inspect in one sitting while still connecting reliability, product outcomes, background work, and AI cost.
The sample does not use production traffic or customer data. Every run has a unique run_id, so its query can isolate the rows it created.
What the demo proves
One wide event contract can answer several questions:
- Did the workflow complete?
- Which step failed or retried?
- How long did each step take?
- How much estimated AI cost did the workflow create?
- Which account plan and release were affected?
The code uses the public HTTP endpoints directly, so there is no framework or SDK abstraction between the event, API request, and SQL result.
Prerequisites
You need Node.js 20 or newer and a Telemetry API key. Export the key only in the shell that will run the sample:
export TELEMETRY_API_KEY="YOUR_API_KEY"
The repository also keeps the runnable source in examples/saas-observability-demo. The complete program is shown below so the data contract and query remain visible on this page.
The complete program
Save this as demo.mjs:
import { randomUUID } from "node:crypto";
const apiKey = process.env.TELEMETRY_API_KEY;
if (!apiKey) {
throw new Error("TELEMETRY_API_KEY is required to run this demo");
}
const apiOrigin = process.env.TELEMETRY_API_ORIGIN || "https://api.telemetry.sh";
const runId = randomUUID();
const table = "saas_observability_demo";
const base = {
run_id: runId,
account_id: "synthetic_acme",
plan: "growth",
release: "demo-2026.07",
region: "us-west",
};
const events = [
{
...base,
event_name: "checkout_started",
workflow: "subscription_checkout",
step: "checkout",
outcome: "started",
duration_ms: 18,
retry_count: 0,
estimated_cost_usd: 0,
},
{
...base,
event_name: "payment_authorized",
workflow: "subscription_checkout",
step: "payment",
outcome: "success",
duration_ms: 284,
retry_count: 0,
estimated_cost_usd: 0,
},
{
...base,
event_name: "invoice_job_completed",
workflow: "subscription_checkout",
step: "invoice_job",
outcome: "success",
duration_ms: 618,
retry_count: 1,
estimated_cost_usd: 0,
},
{
...base,
event_name: "welcome_email_completed",
workflow: "subscription_checkout",
step: "welcome_email",
outcome: "failed",
duration_ms: 910,
retry_count: 2,
error_type: "provider_timeout",
estimated_cost_usd: 0,
},
{
...base,
event_name: "ai_summary_completed",
workflow: "subscription_checkout",
step: "ai_summary",
outcome: "success",
duration_ms: 742,
retry_count: 0,
model: "configured-demo-model",
input_tokens: 820,
output_tokens: 146,
estimated_cost_usd: 0.0042,
},
];
const ingestResponse = await fetch(`${apiOrigin}/log`, {
method: "POST",
headers: {
Authorization: apiKey,
"Content-Type": "application/json",
},
body: JSON.stringify({ table, data: events }),
});
if (!ingestResponse.ok) {
throw new Error(
`Ingest failed: ${ingestResponse.status} ${await ingestResponse.text()}`
);
}
const sql = `
SELECT
workflow,
COUNT(*) AS event_count,
SUM(CASE WHEN outcome = 'failed' THEN 1 ELSE 0 END) AS failed_steps,
SUM(retry_count) AS retries,
SUM(estimated_cost_usd) AS estimated_cost_usd,
MAX(duration_ms) AS slowest_step_ms
FROM ${table}
WHERE run_id = '${runId}'
GROUP BY workflow
ORDER BY workflow
`;
const queryResponse = await fetch(`${apiOrigin}/query`, {
method: "POST",
headers: {
Authorization: apiKey,
"Content-Type": "application/json",
},
body: JSON.stringify({ query: sql, realtime: true, json: true }),
});
if (!queryResponse.ok) {
throw new Error(
`Query failed: ${queryResponse.status} ${await queryResponse.text()}`
);
}
const result = await queryResponse.json();
console.log(JSON.stringify({ run_id: runId, rows: result.data }, null, 2));
Run it:
node demo.mjs
The expected shape is one summary row:
{
"run_id": "generated-for-this-run",
"rows": [
{
"workflow": "subscription_checkout",
"event_count": 5,
"failed_steps": 1,
"retries": 3,
"estimated_cost_usd": 0.0042,
"slowest_step_ms": 910
}
]
}
The exact numeric encoding in the JSON response can vary by query result serialization. Treat the shape and meaning as the contract.
Inspect the raw timeline
An aggregate tells you that the workflow had one failed step. A correlated timeline tells you which step failed and what happened around it:
SELECT
timestamp_utc,
event_name,
step,
outcome,
duration_ms,
retry_count,
error_type
FROM saas_observability_demo
WHERE run_id = 'PASTE_RUN_ID'
ORDER BY timestamp_utc ASC;
The run_id acts like a workflow correlation identifier. In a real application, use a stable identifier created at the workflow boundary and pass it through the API handler, queue payload, job, webhook, and AI call.
Build three views from the same contract
Reliability
Chart failed steps and maximum duration by release or region. Alert only after defining a minimum volume and the response expected from the team.
Product completion
Count distinct workflow identifiers that reached the expected terminal event. Do not count rows as completed workflows when one workflow can emit several steps.
Cost and value
Sum estimated_cost_usd for AI steps and join or correlate it to a later outcome such as activation, accepted output, or successful workflow completion. Keep model pricing in versioned configuration and reconcile estimates to the provider invoice.
Production changes to make
The demo favors visibility over abstraction. A production implementation should:
- create events at real operation boundaries rather than in a demonstration array;
- use a bounded schema and normalize route, error, plan, and release values;
- avoid interpolating untrusted input into SQL;
- keep API keys in the server-side secret store;
- batch or buffer events without hiding permanent failures;
- define retention and deletion requirements;
- record a schema version when producers evolve independently;
- measure whether event delivery itself is failing.
Use Structured Logging for instrumentation, SQL for Observability for the analysis model, and the connected SQL Lab for a larger six-table dataset with visual results.