Expose useful diagnostics without exposing integration secrets
Provide authenticated, bounded dependency status and correlation evidence while redacting credentials, destinations, payloads, and personal or commercial data.
In this guide
Decide who needs which answer
A shopper needs a simple message when an order or integration action cannot proceed. A support engineer may need a correlation ID and a bounded state. An operations owner may need to know whether a dependency is reachable, degraded, or failing. These audiences should not receive the same endpoint or fields. A broad diagnostic response can accidentally disclose endpoints, credentials, order details, or internal topology.
Start by writing the decision each diagnostic supports. For example, an operator may need to decide whether to pause replay, while support needs to connect a customer report to an existing safe record. If a field does not help an authorized reader make a defined decision, leave it out.
- Shopper: safe status and next action.
- Support: correlation ID and bounded outcome.
- Operations: dependency class, elapsed time, safe reason category.
- Administrators: authenticated detail with audited access.
Use bounded dependency checks
A diagnostic check should have a short timeout, a response-size cap, and a narrow operation that does not mutate external state. Avoid calling an ERP transaction endpoint merely to see whether it is alive. Prefer a provider-approved health mechanism, controlled status query, or project-owned delivery-store state. Classify the result as healthy, degraded, unavailable, unauthorized, or unknown, and include when it was checked.
Run checks within concurrency limits. An unbounded health page can become the incident that overloads a struggling dependency. Cache a safe summary for a short, explicitly chosen period when live checks are expensive, and make the freshness timestamp visible to authorized readers.
The serializer below takes unknown internal input and constructs a new response object field by field. Names, states, and reasons come from closed allowlists; timestamps must be canonical UTC ISO strings. It rejects invalid elapsed times, duplicate dependency names, more than three dependencies, and responses over 2 KiB. Generate the UUID v4 correlation ID in the application. Syntax validation prevents malformed values; the generator and upstream mapping ensure that it is an opaque support reference.
serialize-diagnostics.ts typescript
const names = ["erp", "search", "delivery"] as const;
const states = ["healthy", "degraded", "unavailable", "unauthorized", "unknown"] as const;
const reasons = ["ok", "timeout", "authentication", "configuration", "upstream", "pending"] as const;
type Name = typeof names[number];
type State = typeof states[number];
type Reason = typeof reasons[number];
function record(value: unknown): Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value))
throw new TypeError("Expected diagnostic object");
return value as Record<string, unknown>;
}
function member<T extends string>(value: unknown, allowed: readonly T[]): T {
if (typeof value !== "string" || !allowed.includes(value as T))
throw new TypeError("Unsupported diagnostic category");
return value as T;
}
function timestamp(value: unknown): string {
if (typeof value !== "string" || value.length !== 24)
throw new TypeError("Use UTC ISO timestamp");
const parsed = new Date(value);
if (!Number.isFinite(parsed.getTime()) || parsed.toISOString() !== value)
throw new TypeError("Invalid UTC timestamp");
return value;
}
// Call on the server after authorizing the operations reader.
// Input is internal data; rebuild every field before JSON serialization.
export function serializeDiagnostics(input: unknown): string {
const source = record(input);
if (typeof source.correlationId !== "string" ||
!/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
.test(source.correlationId))
throw new TypeError("Use a random UUID v4 support reference");
if (!Array.isArray(source.dependencies) || source.dependencies.length > 3)
throw new RangeError("Too many dependencies");
const seen = new Set<Name>();
const dependencies = source.dependencies.map(value => {
const item = record(value);
const name = member(item.name, names);
if (seen.has(name)) throw new Error("Duplicate dependency");
seen.add(name);
const state: State = member(item.state, states);
const reason: Reason = member(item.reason, reasons);
if (typeof item.elapsedMs !== "number" || !Number.isFinite(item.elapsedMs) ||
item.elapsedMs < 0 || item.elapsedMs > 60000)
throw new RangeError("Invalid elapsed time");
return { name, state, reason, checkedAt: timestamp(item.checkedAt),
elapsedMs: Math.round(item.elapsedMs) };
});
const safe = { correlationId: source.correlationId, dependencies };
const json = JSON.stringify(safe);
if (new TextEncoder().encode(json).byteLength > 2048)
throw new RangeError("Diagnostic response too large");
return json;
}
Redact by design
Do not rely on a later logging filter to remove secrets from a rich object. Create a purpose-built diagnostic DTO with an allowlist of safe fields. Exclude authorization headers, bearer tokens, connection strings, destination URLs, raw request or response bodies, payment data, names, email addresses, SKUs, and customer account identifiers unless an approved internal system has a documented reason to handle them.
Correlation IDs should be random or otherwise safe to disclose. If an external system reference is sensitive, store a mapping internally and show a separate support-safe identifier. Audit access to detailed diagnostic views and ensure log retention follows the organization's policy.
Use this as a server utility behind the Webstore agent or an authorized integration-monitor endpoint. Znode 10's API customization guide identifies the Custom API service boundary for additional backend behavior. If the check runs in C#, map its bounded result into this serializer in the server layer that serves the UI. Keep credential-bearing request objects and exceptions out of that mapping. The example includes no HTTP handler because route registration and authorization belong to the selected host.
- Allowlist response fields.
- Cap text lengths and collection counts.
- Authenticate and authorize each diagnostics route.
- Record access without recording the protected content.
- Test redaction using deliberately sensitive fixture values.
Connect status to recovery
Diagnostics are useful when they lead to an action. For a durable delivery system, show whether work is queued, retrying, dead-lettered, completed, or awaiting reconciliation. Do not promise exactly-once delivery. Provide an authenticated replay or escalation path only after confirming its authorization, idempotency, and audit requirements.
A status endpoint must distinguish an order accepted by commerce from a confirmed external completion. If the answer is unknown, say unknown and guide the operator to reconciliation. That precision prevents a support dashboard from creating false certainty.
Verification checklist
Test unauthorized access, overlong dependency responses, timeout, invalid downstream certificate or configuration, degraded state, and redaction with synthetic secret-like values. Confirm the endpoint cannot trigger a side effect and that no protected value reaches browser logs or monitoring labels. Re-review when an integration, authentication mechanism, or logging platform changes.
References and further reading
Bring your next engineering question.
Need integration observability that respects customer data?
Independent guidance from GCG. Znode is a trademark of its owner. Examples use fictional data and are not official platform documentation. Suggest a correction.