> ## Documentation Index
> Fetch the complete documentation index at: https://docs.squasher.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# HTTP API

> Send logs and errors to Squasher from any language using a simple HTTP POST.

Squasher's ingestion API accepts the Squasher event schema over HTTP. No SDK required — if you can make an HTTP request that sends structured events, you can send data to Squasher.

This is the universal fallback for any language, framework, or platform that doesn't have a dedicated SDK.

If you use the Log Connectors screen in Squasher, create the HTTP API connector first and copy the managed endpoint and ingest key from there. The endpoint below uses the same wire format.

## Endpoint

```
POST https://ingest.squasher.ai/v1/ingest/{project_id}
```

## Authentication

| Header           | Value                                        |
| ---------------- | -------------------------------------------- |
| `x-squasher-key` | Your project API key (`sq_pk_...`)           |
| `Content-Type`   | `application/json` or `application/x-ndjson` |

## Quick Start

Send a test event using curl:

```bash theme={null}
curl -X POST "https://ingest.squasher.ai/v1/ingest/YOUR_PROJECT_ID" \
  -H "Content-Type: application/json" \
  -H "x-squasher-key: sq_pk_your_api_key" \
  -d '{
    "message": "Hello from Squasher!",
    "level": "info",
    "environment": "production",
    "tags": { "service": "my-app" }
  }'
```

Response: `202 Accepted`

```json theme={null}
{
  "id": "evt_550e8400-e29b-41d4-a716-446655440000",
  "status": "accepted"
}
```

## Batch Formats

Send one JSON object for a single event. To send multiple events, use any of these formats:

* A top-level JSON array
* An object containing an `events` array
* Newline-delimited JSON (NDJSON), with one object per non-empty line

For an `events` envelope:

```json theme={null}
{
  "events": [
    { "message": "Order created", "level": "info" },
    { "message": "Payment failed", "level": "error", "type": "PaymentError" }
  ]
}
```

The equivalent top-level array is:

```json theme={null}
[
  { "message": "Order created", "level": "info" },
  { "message": "Payment failed", "level": "error", "type": "PaymentError" }
]
```

For NDJSON, set `Content-Type: application/x-ndjson`:

```jsonl theme={null}
{"message":"Order created","level":"info"}
{"message":"Payment failed","level":"error","type":"PaymentError"}
```

Empty bodies, empty batches, malformed JSON, and non-object events return `400 Bad Request` and
are not forwarded.

## Payload Fields

| Field         | Type   | Required | Description                                                    |
| ------------- | ------ | -------- | -------------------------------------------------------------- |
| `message`     | string | **Yes**  | Log message or error description                               |
| `type`        | string | No       | Error class name (e.g. `TypeError`, `ConnectionError`)         |
| `level`       | enum   | No       | `fatal`, `error`, `warning`, `info`, `debug`. Default: `error` |
| `stack`       | string | No       | Raw stack trace string                                         |
| `frames`      | array  | No       | Parsed stack frames (preferred over raw `stack`)               |
| `environment` | string | No       | Environment tag (e.g. `production`, `staging`)                 |
| `release`     | string | No       | Release/version string (e.g. `v1.2.3`)                         |
| `tags`        | object | No       | Arbitrary key-value metadata                                   |
| `user`        | object | No       | User context: `id`, `email`, `username`, `ip_address`          |
| `request`     | object | No       | Request context: `url`, `method`, `headers`                    |
| `breadcrumbs` | array  | No       | Trail of events leading to the error                           |
| `extra`       | object | No       | Arbitrary extra data                                           |
| `timestamp`   | string | No       | ISO 8601 timestamp. Default: server receive time               |

## Log Levels

Squasher processes all log levels but treats them differently:

| Level     | Indexed for Search | Creates Error Group | Triggers Alerts | Counts Against |
| --------- | ------------------ | ------------------- | --------------- | -------------- |
| `fatal`   | Yes                | Yes                 | Yes             | Error quota    |
| `error`   | Yes                | Yes                 | Yes             | Error quota    |
| `warning` | Yes                | No                  | No              | Log quota      |
| `info`    | Yes                | No                  | No              | Log quota      |
| `debug`   | Yes                | No                  | No              | Log quota      |

Non-error events provide context for AI triage — when an error occurs, Squasher uses surrounding log entries to build a richer diagnosis.

## Examples

### Error with Stack Trace

```bash theme={null}
curl -X POST "https://ingest.squasher.ai/v1/ingest/YOUR_PROJECT_ID" \
  -H "Content-Type: application/json" \
  -H "x-squasher-key: sq_pk_your_api_key" \
  -d '{
    "message": "Cannot read properties of undefined",
    "type": "TypeError",
    "level": "error",
    "stack": "TypeError: Cannot read properties of undefined\n    at UserList (src/UserList.tsx:24:18)",
    "frames": [
      {
        "filename": "src/UserList.tsx",
        "function": "UserList",
        "lineno": 24,
        "colno": 18,
        "in_app": true
      }
    ],
    "environment": "production",
    "release": "v1.2.3",
    "user": { "id": "user_42", "email": "user@example.com" }
  }'
```

### Structured Log Entry

```bash theme={null}
curl -X POST "https://ingest.squasher.ai/v1/ingest/YOUR_PROJECT_ID" \
  -H "Content-Type: application/json" \
  -H "x-squasher-key: sq_pk_your_api_key" \
  -d '{
    "message": "Order processed successfully",
    "level": "info",
    "tags": {
      "order_id": "ord_12345",
      "amount": 49.99,
      "currency": "USD"
    }
  }'
```

### Python

```python theme={null}
import requests

requests.post(
    "https://ingest.squasher.ai/v1/ingest/YOUR_PROJECT_ID",
    headers={
        "Content-Type": "application/json",
        "x-squasher-key": "sq_pk_your_api_key",
    },
    json={
        "message": "Database connection failed",
        "type": "ConnectionError",
        "level": "error",
        "tags": {"db_host": "db.example.com", "retry_count": 3},
    },
)
```

### Go

```go theme={null}
payload := map[string]interface{}{
    "message": "Request timeout exceeded",
    "type":    "TimeoutError",
    "level":   "error",
    "tags":    map[string]interface{}{"endpoint": "/api/users", "duration_ms": 30000},
}
body, _ := json.Marshal(payload)

req, _ := http.NewRequest("POST",
    "https://ingest.squasher.ai/v1/ingest/YOUR_PROJECT_ID", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("x-squasher-key", "sq_pk_your_api_key")

http.DefaultClient.Do(req)
```

### Ruby

```ruby theme={null}
require "net/http"
require "json"

uri = URI("https://ingest.squasher.ai/v1/ingest/YOUR_PROJECT_ID")
req = Net::HTTP::Post.new(uri, {
  "Content-Type" => "application/json",
  "x-squasher-key" => "sq_pk_your_api_key",
})
req.body = { message: "Unexpected nil value", type: "NoMethodError", level: "error" }.to_json
Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
```

## Rate Limits

* **Edge:** 1 MB max payload per request for SDK/drain routes
* **OTLP:** 5 MB max payload for `/v1/traces` and `/v1/logs`
* **Project admission limit** — batches that exceed `max_events_per_minute` return `429` with
  `Retry-After` and `X-RateLimit-*` headers; retry after the advertised reset

## Combining with SDKs

The HTTP API accepts the same payload format as Squasher's SDKs. You can mix and match — use the SDK for your primary language and the HTTP API for auxiliary services. All events appear in the same dashboard and are deduplicated by fingerprint.

## AI batch ingest

For LLM generations, agent sessions, and tool calls, prefer the [Agent telemetry SDK](/sdks/agent). If you need direct HTTP, send Langfuse-compatible AI batches to `POST /v1/ai/ingest/{project_id}`. The [OpenRouter integration](/integrations/openrouter#direct-http-ingest-no-sdk) has a complete curl payload.

## Agent handoff

```text theme={null}
Use Squasher HTTP ingest only when no first-party SDK or OTLP exporter fits. Read SQUASHER_API_KEY and SQUASHER_PROJECT_ID from environment variables, send one safe verification event, then confirm it appears with logs or errors search. Do not hardcode real API keys.
```
