Developer guide
Install @nzila/sdk@0.1.14+, wire reporters, send runs, instrument runtime/VESL/DOM signals, and use feature intelligence in the dashboard.
Set up in the dashboard
- Create a project under Projects and copy its App name — every payload must use this exact appName.
- Under API Keys, generate a key for that project. Copy it when shown; you cannot view it again.
- Use the project switcher in the header to choose which project you are viewing; keys only ingest into their own project.
Hosted platform
Production dashboard: https://nzila-kappa.vercel.app — sign in, create projects, and manage API keys. After ingestion, use Regression insights, Release health, Journeys, global search (Ctrl/Cmd+K), and execution replay on each run.
Install & configure @nzila/sdk
Reporters send one webhook per finished run. Use nzila.config.ts from the CLI or environment variables in CI. Set NZILA_DEBUG=1 if nothing arrives.
Install
npm install @nzila/sdkVitest projects need vitest as a peer. TypeScript: use moduleResolution bundler, node16, or nodenext for @nzila/sdk/* subpaths.
CLI
# Open the hosted dashboard and print setup steps
npx @nzila/sdk
# Sign in, then link this repo to a project
npx @nzila/sdk login
npx @nzila/sdk link --api-key nzl_… --project-id <uuid> --app-name my-app
# Verify nzila.config.ts and integration
npx @nzila/sdk doctor
npx @nzila/sdk repair --dry-run
npx @nzila/sdk repair --apply
# Roll back Vitest config patches: npx @nzila/sdk repair --rollbacknzila.config.ts
Copy project ID from API Keys after creating a key. locale drives AI run summaries, grouped test steps, failure analysis, and PDF export text.
// nzila.config.ts (created by npx @nzila/sdk link)
export default {
apiKey: "nzl_xxxxxxxx",
projectId: "00000000-0000-0000-0000-000000000000",
appName: "my-app", // must match Projects → App name
endpoint: "https://nzila-kappa.vercel.app",
framework: "vitest",
locale: "en", // en | pt | fr | zh — AI summaries & failure copy
} as const;Vitest (recommended)
Use nzilaVitestReporter(reporterOptionsFromNzilaConfig(...)) — do not use new NzilaVitestReporter() in vitest.config (worker serialization).
import { defineConfig } from "vitest/config";
import { nzilaVitestReporter } from "@nzila/sdk/vitest";
import { reporterOptionsFromNzilaConfig } from "@nzila/sdk";
import nzilaConfig from "./nzila.config";
// Important: use nzilaVitestReporter(...) — never new NzilaVitestReporter()
// (Vitest worker pools cannot serialize class instances).
export default defineConfig({
test: {
reporters: [
"default",
nzilaVitestReporter(reporterOptionsFromNzilaConfig(nzilaConfig)),
],
},
});Or pass webhookUrl, apiKey, appName, and projectId directly:
import { defineConfig } from "vitest/config";
import { nzilaVitestReporter } from "@nzila/sdk/vitest";
export default defineConfig({
test: {
reporters: [
"default",
nzilaVitestReporter({
webhookUrl: process.env.NZILA_WEBHOOK_URL!,
apiKey: process.env.NZILA_API_KEY!,
appName: process.env.NZILA_APP_NAME!,
projectId: process.env.NZILA_PROJECT_ID,
locale: "en",
maxRetries: 3,
}),
],
},
});Jest
Use the tuple form ["@nzila/sdk/jest", options] in jest.config.cjs or jest.config.js.
/** @type {import('jest').Config} */
module.exports = {
testEnvironment: "node",
reporters: [
"default",
[
"@nzila/sdk/jest",
{
webhookUrl: process.env.NZILA_WEBHOOK_URL,
apiKey: process.env.NZILA_API_KEY,
appName: process.env.NZILA_APP_NAME,
projectId: process.env.NZILA_PROJECT_ID,
framework: "jest",
locale: "en",
// feature names auto-detected from test file + describe()
},
],
],
};Mocha
const { createMochaNzilaReporter } = require("@nzila/sdk/mocha");
module.exports = {
reporter: createMochaNzilaReporter({
webhookUrl: process.env.NZILA_WEBHOOK_URL,
apiKey: process.env.NZILA_API_KEY,
appName: process.env.NZILA_APP_NAME,
framework: "mocha",
locale: "en",
}),
};Manual / custom CI
Build the same tests[] array and call sendNzilaRun from Node after your suite finishes.
import { sendNzilaRun } from "@nzila/sdk";
await sendNzilaRun(
{
runId: "ci-123",
appName: "my-app",
framework: "other",
locale: "pt",
startedAt: new Date().toISOString(),
finishedAt: new Date().toISOString(),
tests: [
{
appName: "my-app",
framework: "other",
startedAt: "...",
finishedAt: "...",
describePath: ["Payment API", "pricing"],
testName: "applies coupon to subtotal",
status: "passed",
duration: 12,
errors: [],
},
],
},
{
webhookUrl: process.env.NZILA_WEBHOOK_URL!,
apiKey: process.env.NZILA_API_KEY!,
maxRetries: 3,
},
);Environment variables
# Required for test reporters (CI secrets)
NZILA_API_KEY=nzl_xxxxxxxx
NZILA_PROJECT_ID=00000000-0000-0000-0000-000000000000
NZILA_WEBHOOK_URL=https://your-nzila-host/api/webhooks/tests
NZILA_APP_NAME=my-app
NZILA_LOCALE=en
# Runtime signals (optional — Feature health)
NZILA_SIGNALS_URL=https://your-nzila-host/api/signals/runtime
NEXT_PUBLIC_NZILA_SIGNALS_URL=https://your-nzila-host/api/signals/runtime
NEXT_PUBLIC_NZILA_API_KEY=nzl_xxxxxxxx
# Debug reporter delivery (stderr)
# NZILA_DEBUG=1Interactive dashboard guide
Each workspace section (Overview, Projects, Runs, Feature health, and the rest) has a step-by-step tour in your UI language.
- The first time you open a section, the tour starts automatically and explains every control on that page.
- Finish the last step (Done) to mark the section complete; closing early will show the tour again on your next visit.
- Use Guide in the header anytime to replay the tour for the current page.
Authentication
Send your project API key on every request. Keys are tied to one project and can expire.
Authorization: Bearer nzl_your_api_keySend a completed test run
Call this endpoint once per finished run (all tests in one JSON body). Use your Nzila host URL in production.
POST /api/webhooks/testsRe-sending the same runId for the same project is safe — the API returns 200 and does not duplicate the run.
JSON body
- runId — unique id from your CI or test runner (UUID recommended).
- appName — must match the project's App name in Nzila.
- framework — vitest, jest, mocha, playwright, jasmine, cypress, karma, node-test, or other.
- startedAt / finishedAt — ISO 8601 timestamps for the whole run.
- tests[] — each item: describePath, testName, status (passed | failed | skipped | pending), duration (ms), errors[{ message, stack? }].
- locale (optional) — en, pt, fr, or zh: all AI summaries, failure analysis, and report outlines for that run are generated in this language.
- Per test: optional feature override, context (e.g. skipReason for skipped tests).
{
"runId": "ci-run-2026-05-16-abc123",
"appName": "my-app",
"framework": "vitest",
"startedAt": "2026-05-16T14:00:00.000Z",
"finishedAt": "2026-05-16T14:00:04.200Z",
"locale": "pt",
"tests": [
{
"describePath": ["LikesModal – desktop"],
"testName": "renders Modal on desktop",
"feature": "Social",
"status": "passed",
"duration": 120,
"errors": []
},
{
"describePath": ["ActivateAccountModal"],
"testName": "calls reset on modal open",
"status": "failed",
"duration": 45,
"errors": [
{
"message": "AssertionError: expected \"vi.fn()\" to be called 1 times, but got 0 times"
}
]
},
{
"describePath": ["Checkout UI", "coupons"],
"testName": "skips when flag off",
"status": "skipped",
"duration": 0,
"context": { "skipReason": "feature flag disabled" },
"errors": []
}
]
}HTTP responses
- 202 — Run accepted and queued for metrics and insights.
- 200 — Same runId already ingested (idempotent retry).
- 401 / 403 — Invalid, expired, or revoked key (codes API_KEY_INVALID, API_KEY_EXPIRED, API_KEY_REVOKED).
- 400 — Invalid JSON or schema validation errors in the body.
Example (curl)
curl -X POST "$NZILA_WEBHOOK_URL/api/webhooks/tests" \
-H "Authorization: Bearer $NZILA_API_KEY" \
-H "Content-Type: application/json" \
-d @run-payload.jsonTest runners
Use the same webhook from any runner. Nzila ships reporters for Vitest, Jest, and Mocha; everything else can POST the JSON payload (Python, Go, .NET, etc.).
Playwright, Cypress, and Karma: export results after the run and POST with framework set to playwright, cypress, or karma, or use framework other.
npm install @nzila/sdk — reporters under @nzila/sdk/vitest, @nzila/sdk/jest, @nzila/sdk/mocha; CLI via npx @nzila/sdk.
Full Vitest, Jest, and Mocha examples are in the Install & configure section above.
Trace test failures to a feature
Every test is identified by describePath + testName (fingerprint). Map suites to product features, then follow failures from the dashboard back to the exact it() block and error output.
Fingerprint = suite path + test name
Nested describe() segments become describePath[]. The fingerprint is joined with › and shown in Stability and run details.
// Vitest — suite path becomes describePath[] in the webhook
describe("ActivateAccountModal", () => {
it("calls reset, invalidates query and tracks on modal open", async () => {
// ...
expect(vi.fn()).toHaveBeenCalledTimes(1); // failure → message in errors[]
});
});
// Dashboard fingerprint (stable id):
// ActivateAccountModal › calls reset, invalidates query and tracks on modal openTrace a failed test (step by step)
- Run tests with the Nzila reporter and locale set (so failure text matches your team language).
- Open Test runs → pick the run → What was tested: grouped steps mirror describe() blocks.
- Click the status pill on a failed step for plain-language why, technical Vitest/Jest output, and bad-test hints (e.g. mock not called).
- Open Failure analysis for the same run — AI detail is stored per locale.
- In the repo, search for the fingerprint or describe title + it() name; fix the product or the test setup.
AssertionError and vi.fn() / toHaveBeenCalled failures are flagged as possible test issues, not only product bugs.
Attach tests to a feature
- With the Nzila Vitest reporter, each test’s feature is auto-detected from the file name (e.g. activate-account-modal.test.tsx → Activate Account Modal) and describe() blocks.
- You only need mapFeature when you want to override that default.
- Optional: set feature on each test in a manual JSON payload.
import { defineConfig } from "vitest/config";
import { nzilaVitestReporter } from "@nzila/sdk/vitest";
import { reporterOptionsFromNzilaConfig } from "@nzila/sdk";
import nzilaConfig from "./nzila.config";
// Important: use nzilaVitestReporter(...) — never new NzilaVitestReporter()
// (Vitest worker pools cannot serialize class instances).
export default defineConfig({
test: {
reporters: [
"default",
nzilaVitestReporter(reporterOptionsFromNzilaConfig(nzilaConfig)),
],
},
});Where to trace in the dashboard
- Execution Overview — health and recent failures.
- Test runs — open a run; see every test status and label.
- Failure analysis — readable messages from failed tests (not raw stacks).
- Stability metrics — pass rate, flake, regressions per check.
- Features — aggregates by feature name.
- In your repo — match describePath and testName to describe / it blocks.
- Export PDF — same grouped steps and plain-language descriptions for stakeholders.
Runtime tracing (frontend and backend)
After tests are wired, instrument live code with the SDK. Call one entry at the top of the screen or API handler; use captureError in your own catch blocks. Signals use the same feature string as Vitest.
POST /api/signals/runtimeInitialize once (server or app bootstrap)
import { initNzilaRuntime } from "@nzila/sdk";
// Node / Route Handlers only — not at module scope in React
initNzilaRuntime({
signalsUrl: process.env.NZILA_SIGNALS_URL!,
apiKey: process.env.NZILA_API_KEY!,
locale: "en",
});React — useNzilaFeature at the top of the component
Wrap the app with NzilaProvider (do not call initNzilaRuntime at module scope in React). useNzilaFeature must be the first hook in components that own the feature — same string as mapFeature / describe.
"use client";
import { NzilaProvider } from "@nzila/sdk/react";
export function AppProviders({ children }: { children: React.ReactNode }) {
return (
<NzilaProvider
signalsUrl={process.env.NEXT_PUBLIC_NZILA_SIGNALS_URL!}
apiKey={process.env.NEXT_PUBLIC_NZILA_API_KEY!}
locale="en"
>
{children}
</NzilaProvider>
);
}"use client";
import { useNzilaFeature } from "@nzila/sdk/react";
export function CheckoutPage() {
const nzila = useNzilaFeature("Checkout");
async function pay() {
try {
await nzila.runApi("POST /api/checkout", () =>
fetch("/api/checkout", { method: "POST" }),
);
} catch (err) {
nzila.captureError(err, { step: "pay", handled: true });
}
}
}Also detects likely render loops (too many re-renders), unhandled errors, and promise rejections while mounted.
Backend — traceNzilaFeature at the top of the handler
First line inside the route or service that matches your tested feature. Wrap outbound calls with runApi or call captureError in catch.
import { traceNzilaFeature } from "@nzila/sdk";
export async function POST(req: Request) {
const nzila = traceNzilaFeature("Checkout");
try {
const res = await nzila.runApi("charge", () => charge(req));
return Response.json({ ok: true, data: res });
} catch (err) {
nzila.captureError(err, { route: "POST /checkout" });
return Response.json({ error: "failed" }, { status: 502 });
}
}Manual errors (shared helpers)
When you already handle the error (toast, 4xx response), still report it:
import { reportFeatureError } from "@nzila/sdk";
reportFeatureError("Checkout", error, { layer: "validation" });Handle API
- captureError(error, detail?) — any caught error (type: error).
- captureApiError(error, label, detail?) — HTTP/upstream failures you caught without runApi.
- runApi(label, fn) — wraps async calls; records api success/failure.
- reportFeatureError(feature, error, detail?) — from utilities when you only have feature + error.
Open Feature health to compare test results with live api, ui (render_loop), and error signals for that feature.
Feature intelligence & execution observability
Nzila goes beyond pass/fail reporting: correlate tests with runtime signals, replay execution, detect regressions, and explain impact in plain language (en, pt, fr, zh).
DOM snapshot replay
Lightweight JSON snapshots (component tree, modals, toasts, loading). SDK 0.1.14+ captures on interaction when VESL auto-instrumentation is active; manual POST supported.
POST /api/signals/dom-snapshots
# Lightweight UI state (not video). Linked to runs via externalRunId / runId.
{
"externalRunId": "same-as-webhook-runId",
"snapshots": [{
"feature": "Checkout",
"route": "/checkout",
"componentName": "PaymentStep",
"snapshot": { "version": 1, "loading": false, "modals": [], "tree": [] }
}]
}Snapshots share externalRunId with the test run and appear in the replay panel when you scrub the timeline.
Regression intelligence
After each processed run, degraded features are compared to the last healthy run. Suspected deployments fall between healthy and degraded timestamps.
- POST deployments from CI so regressions can name a commit/version.
- Dashboard: Regression insights — summary, first degraded run, suspected component.
- Root-cause hints are structured categories (navigation, cache, race) — not a chat UI.
curl -X POST "$NZILA_URL/api/deployments" \
-H "Authorization: Bearer $NZILA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"version":"1.4.2","commitSha":"abc123","environment":"production"}'Release health score
Aggregates feature_correlations into a health score and stable / at-risk / degraded lists. Recalculate from the Release health page after major deploys.
Flaky test detection
Stability metrics drive flake scores; the regression page shows a flaky leaderboard. Stability Metrics remains the full per-check history.
Failure triage
On Failure analysis and run failure cards: acknowledge flaky behavior or mark false positives. Activity is scoped to your organization and project.
POST /api/test-results/<resultId>/annotations — acknowledge_flaky | false_positiveFeature dependencies
Store edges between features (depends_on) via API or future SDK signals; view the dependency table under Dependencies.
CLI doctor & auto-repair
Diagnose missing Vitest reporter wiring and apply safe config patches with rollback.
npx @nzila/sdk doctor # config + reporter diagnostics
npx @nzila/sdk repair --dry-run # list fixable issues
npx @nzila/sdk repair --apply # patch vitest.config (creates .nzila.bak)
npx @nzila/sdk repair --rollback # restore from backupDashboard pages
Sign in to open these workspace pages.
CI or custom HTTP client
Build the same JSON payload from any runner (GitHub Actions, GitLab, Jenkins, etc.) and POST after the suite finishes.
Run the worker on your Nzila deployment so ingestion is processed into overview, stability, and failure analysis.
Using the product after ingestion
Once a run is accepted, open the dashboard for that project:
- Execution Overview — pass/fail counts, AI run summary, and recent runs.
- Failure Analysis — friendly explanations for failed checks.
- Stability Metrics — pass rate, flake score, and regression flags per test.
- Performance Trends — duration deltas when historical data exists.
- Switch projects in the header to compare apps or services separately.
- Feature health — test vs runtime signals when you use useNzilaFeature or traceNzilaFeature.
- Interactive Guide — first-visit tours per section; empty states explain what to do when a list has no data yet.
- What was tested — describe() groups as suites with numbered steps and AI/plain descriptions.
- PDF export — grouped steps per run for a date range, localized via payload/reporter locale.
- Regression insights — degraded features, suspected deploys, flaky leaderboard.
- Release health — portfolio score with stable, at-risk, and degraded feature lists.
- Execution replay — human & developer views, phased timeline, Vitest step events, DOM frames.
- Failure triage — acknowledge flaky tests or mark false positives on failures.
Language for insights
Set locale on the webhook payload, nzila.config.ts, or reporter options (en | pt | fr | zh). Nzila stores execution summaries and failure analysis per locale. The dashboard header language picks which locale to display; they should match for consistent AI copy.
Security
Never put API keys in browser bundles or public repos. Call the webhook from CI, a secure server, or your test runner process only.