Skip to main content
Use the HTTP API when no SDK or OpenTelemetry exporter fits your stack. It has two endpoints: one for events that your code sends, and one for log lines from a host or a log forwarder.

Authenticate requests

Both endpoints read the ingest key from one of these places:
  • The x-squasher-key: <key> header.
  • The Authorization: Bearer <key> header.
  • The key query parameter (?key=<key>), for tools that cannot set headers.
The project ID in the path must belong to the key. If it does not, Squasher returns 403. To get both values, see Create an ingest key.

Send an event

A 200 response means that Squasher accepted the event. An event at error or fatal level becomes an issue. To send more than one event, use one of these bodies:
  • A JSON array of event objects.
  • An object with an events array.
  • One event object on each line, with Content-Type: application/x-ndjson.
Every event needs a string message. If an event or line is not valid, Squasher rejects the full request with 400 and names the event or line.

Event fields

Automatic AI triage runs only on issues in the production environment by default. Set environment on events that must be triaged.

Send log lines to the drain endpoint

The drain endpoint takes JSON log records in any shape and keeps a few known fields. The platform guides, such as Vercel and Fly.io, use it.
  • <source> is a name that you choose, with lowercase letters, digits, _, and -. Squasher stores it on each record and uses it as the service name when a record has none.
  • Send a JSON array of objects, or one JSON object on each line. Squasher skips lines that are not JSON objects. A JSON array must contain only objects.
  • Syslog bodies also work: Heroku application/logplex-1 octet-counted frames, or one RFC 5424 message on each line. See Heroku.
  • The body can be gzip or deflate compressed. Squasher also detects gzip without a Content-Encoding header. Other encodings return 415. A body can be at most 16 MiB, before and after decompression. A larger body returns 413.
  • Add ?environment=<name> to the URL to set the environment of records that have none. The name can contain letters, digits, ., _, and -, up to 64 characters.
  • Squasher skips a record that has "squasher.skip_ingest": true.

What Squasher keeps from each record

Squasher drops all other fields, including nested objects such as proxy or metadata. It reads only top-level keys, except the Fly.io log.level and fly.app.name fields. Squasher reads a numeric timestamp below 1010 as seconds, below 1016 as milliseconds, and above that as nanoseconds. A timestamp in microseconds is read as milliseconds, so convert it before you send it.

How levels become issues

A top-level level or severity is used as sent. A record without one gets its level from, in order:
  1. A platform level that is not info: the Fly.io log.level, or the syslog severity.
  2. The message: a JSON "level" field (pino numbers too), level=error or at=error, the first uppercase level word such as ERROR, [WARN], or FATAL:, or a stack trace such as Traceback (most recent call last):, TypeError: ..., or panic:.
  3. info, when nothing matches.
Squasher does not set a level from an HTTP status code, and it does not link drain records by request ID. Automatic AI triage runs only on issues with an enabled environment, so set ?environment= when your records have no environment field. Otherwise, select Run triage in the Squasher triage card on the issue to start it by hand. Squasher groups error records into issues by service, error type, and the first line of the message. Before it compares messages, it replaces numbers, hexadecimal strings, and UUIDs.

Limits

  • Each project can send 10,000 records per minute by default. Squasher counts each log record, span, and metric point. Above the limit, it returns 429 with Retry-After and X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers.
  • A 503 response with Retry-After: 1 means that ingest is not available for a moment. Retry with backoff.
  • The events endpoint reads the body as uncompressed text. The drain endpoint also reads gzip and deflate bodies.
For LLM and agent telemetry, use the Agent telemetry SDK. To send Langfuse-compatible batches without it, post them to POST /v1/ai/ingest/<project-id>.

Next steps

OpenTelemetry

Send traces, logs, and metrics from any language over OTLP.

Vector

Forward logs from files and containers through a Vector sink.

Node.js SDK

Capture errors with stack traces from Node.js services.

Issues

See how Squasher groups errors into issues.