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

# POST /api/ingest/ndjson - ingest log events

> Send newline-delimited JSON log events to the index attached to your ingest API key. Requires a bearer API key.

The ingest endpoint accepts a batch of log events as NDJSON and ingests them into the index attached to your ingest API key. Your log shippers authenticate with a per-index ingest API key, so you manage access centrally and each key writes only to its own index.

If you want the full setup flow, see [Send Logs](/send-logs/overview).

## Endpoint

```http theme={"theme":"github-light"}
POST /api/ingest/ndjson
```

## Authentication

```http theme={"theme":"github-light"}
Authorization: Bearer <your-ingest-token>
```

You must include a valid ingest API key in every request. Create ingest keys in the Rootprint web UI at **Settings → API keys**. Each key is scoped to exactly one index; Rootprint uses that index as the ingestion target.

## Request body

Set `Content-Type: application/x-ndjson` (or `application/json`). The body must contain one JSON object per line. Each line represents a single log event. The body cannot be empty. Gzip-compressed bodies are supported: send `Content-Encoding: gzip` and Rootprint forwards it to Quickwit unchanged.

<Note>
  NDJSON (Newline Delimited JSON) means each log entry is a complete JSON object on its own line,
  with a newline character (`\n`) between entries. There is no wrapping array.
</Note>

## Example request

```bash theme={"theme":"github-light"}
curl -X POST 'https://your-rootprint-host/api/ingest/ndjson' \
  -H 'Authorization: Bearer lwit_abc123' \
  -H 'Content-Type: application/x-ndjson' \
  --data-binary @- <<'EOF'
{"timestamp_nanos":1776340800000000000,"severity_text":"INFO","body":{"message":"User logged in"},"service_name":"frontend","attributes":{"user_id":"alice"},"resource_attributes":{},"trace_id":"","span_id":"","trace_flags":0,"observed_timestamp_nanos":1776340800000000000,"severity_number":9,"dropped_attributes_count":0,"resource_dropped_attributes_count":0,"scope_name":"","scope_version":"","scope_attributes":{},"scope_dropped_attributes_count":0}
{"timestamp_nanos":1776340860000000000,"severity_text":"ERROR","body":{"message":"Connection timeout"},"service_name":"api-gateway","attributes":{},"resource_attributes":{},"trace_id":"","span_id":"","trace_flags":0,"observed_timestamp_nanos":1776340860000000000,"severity_number":17,"dropped_attributes_count":0,"resource_dropped_attributes_count":0,"scope_name":"","scope_version":"","scope_attributes":{},"scope_dropped_attributes_count":0}
EOF
```

<Tip>
  Rootprint targets the index configured on the ingest API key. Check the key in **Settings → API keys** if you need to confirm where a shipper writes.
</Tip>

## Response

On success, Rootprint returns a `200` response. Authentication errors (`401`, `403`) are generated by Rootprint. Success responses and `4xx` indexing errors (including `400` for an empty or unparseable body) come from the search engine.

<ResponseField name="num_docs_for_processing" type="number">
  The number of log events accepted for indexing in this request.
</ResponseField>

Example success response:

```json theme={"theme":"github-light"}
{ "num_docs_for_processing": 2 }
```

<Note>
  `num_docs_for_processing` is the count of documents accepted for indexing, not the count
  successfully indexed. If individual documents fail validation against the index schema, the
  search engine logs the failure on the server but the API response stays the same. Inspect the
  search engine's stdout (or your log shipper) when you suspect documents are being silently dropped.
</Note>

## Error responses

| Status | Cause                                                                                                        |
| ------ | ------------------------------------------------------------------------------------------------------------ |
| `400`  | The request body is empty or the payload could not be parsed                                                 |
| `401`  | The `Authorization` header is missing or does not contain a bearer API key                                   |
| `403`  | The bearer key is not a valid ingest key (unknown or revoked key)                                            |
| `404`  | The target index no longer exists                                                                            |
| `413`  | A proxy or upstream service rejected the request as too large (Rootprint sets no body-size limit of its own) |
| `500`  | Rootprint encountered an internal error                                                                      |
| `503`  | Rootprint could not reach Quickwit, or Quickwit is unhealthy or unavailable                                  |

App-level error responses include a JSON body with an `error` object:

```json theme={"theme":"github-light"}
{
  "error": {
    "code": "INGEST_INVALID_TOKEN",
    "message": "Invalid ingest token",
    "statusCode": 403,
    "requestId": "req_abc123"
  }
}
```
