Docs
Install, throw an error, and let Signal0 decide when it matters.
There are only two moving parts in the MVP: a project key in the request header and a minimal JSON payload. That keeps infra cheap and onboarding fast.
Next.js route handlersignal0
import { createSignal0 } from "./signal0";
const signal0 = createSignal0({
endpoint: "https://your-domain.com/api/v1/events",
projectKey: process.env.SIGNAL0_PROJECT_KEY!,
environment: process.env.NODE_ENV,
release: process.env.VERCEL_GIT_COMMIT_SHA,
});
export async function GET() {
try {
throw new Error("Database is out of sync");
} catch (error) {
await signal0.captureError(error, {
route: "/api/orders/sync",
requestId: crypto.randomUUID(),
context: {
job: "orders-sync",
tenant: "launch-demo",
},
});
}
return Response.json({ ok: true });
}`POST /api/v1/events`
Required header: x-project-key
Required body fields: errorType, message
Optional fields: stack, timestamp, environment, release, route, requestId, context
Duplicate events are grouped with the fingerprint formula errorType + message + normalizedStack + primaryContext.
Node scriptsignal0
import { createSignal0 } from "./signal0.js";
const signal0 = createSignal0({
endpoint: "https://your-domain.com/api/v1/events",
projectKey: process.env.SIGNAL0_PROJECT_KEY,
environment: "production",
release: "cron@2026-03-27T16:00:00Z",
});
async function run() {
try {
throw new Error("Nightly import failed");
} catch (error) {
await signal0.captureError(error, {
route: "cron:nightly-import",
requestId: crypto.randomUUID(),
context: {
source: "billing",
batchSize: 1200,
},
});
}
}
run();Tiny SDK sourcesignal0
export function createSignal0(config) {
async function send(payload) {
await fetch(config.endpoint, {
method: "POST",
headers: {
"content-type": "application/json",
"x-project-key": config.projectKey,
},
body: JSON.stringify({
...payload,
environment: payload.environment ?? config.environment,
release: payload.release ?? config.release,
}),
});
}
return {
captureMessage(message, context = {}) {
return send({
errorType: "Message",
message,
context: context.context,
route: context.route,
requestId: context.requestId,
});
},
captureError(error, context = {}) {
return send({
errorType: error?.name ?? "Error",
message: error?.message ?? String(error),
stack: error?.stack,
context: context.context,
route: context.route,
requestId: context.requestId,
});
},
wrap(handler) {
return async (...args) => {
try {
return await handler(...args);
} catch (error) {
await this.captureError(error);
throw error;
}
};
},
};
}