Errors API
Send your app's errors to Skyfleet. Each new one becomes an issue, with an agent on it.
POST /api/v1/signals
Authorization: Bearer sig_…
Content-Type: application/jsonYou'll find the token on the source's page under Integrations: App errors, Better Stack, ClickStack or Cloudflare. Each page makes its own tokens, and the token says which tool is calling. It works from the browser too.
Body
| Field | Type | |
|---|---|---|
service | string, up to 60 | Required. Which app, like api or worker |
message | string, up to 4000 | Required. The error message |
stack | string, up to 50,000 | The stack trace. Used for grouping |
fingerprint | string, up to 200 | Your own grouping key |
kind | string | error, the default and only kind |
status | string | firing, the default, raises the issue. Anything else, like resolved, is accepted and ignored |
Better Stack and Cloudflare tokens take those tools' own webhook bodies instead, so they need no template: a Better
Stack incident becomes one issue per incident (resolves and comments are ignored), and a Cloudflare health check
one issue per check (going healthy is ignored, and so is Cloudflare's test message). Cloudflare can't set an
Authorization header, so Skyfleet also takes the token from cf-webhook-auth.
Response
{ "issue": "is_8f2kQ…", "agent": true }You'll get a 202 back. agent is true if this error started a new agent.
| Status | When |
|---|---|
401 | Unknown token |
400 | A kind other than error, or a missing field |
413 | Body over 256 KB |
Grouping
Skyfleet groups repeats of the same error into one issue. By default it looks at the service, the error type and the
first line of the stack trace that's in your own code, ignoring line numbers, so the same bug still groups after a
new release. Send your own fingerprint if you'd rather group them differently.
Only a brand-new issue, or a resolved one that comes back, starts an agent. Ignored issues just keep counting.
Example
process.on('uncaughtException', (err) => {
if (!process.env.SIGNALS_TOKEN) return
fetch(process.env.SIGNALS_URL!, {
method: 'POST',
headers: { authorization: `Bearer ${process.env.SIGNALS_TOKEN}`, 'content-type': 'application/json' },
body: JSON.stringify({ service: 'api', message: err.message, stack: err.stack }),
signal: AbortSignal.timeout(2000),
}).catch(() => {})
})Send it and move on: use a short timeout, never let a failed report crash your app, and skip it when the token isn't set. Never send request bodies, cookies or your users' data.