Skip to main content
Rootprint exposes an authenticated HTTP endpoint at /api/ingest/ndjson that forwards your request body straight to the index your API key is scoped to. Rootprint adds nothing to the records and reshapes nothing — whatever you POST is what Quickwit indexes. That makes it the endpoint for custom-schema indexes: you define the fields your logs carry, then send records that match. Each ingest API key is scoped to a single index.
For OpenTelemetry-instrumented apps, prefer the OTLP endpoint. It is tied to the OTEL schema (the built-in otel-logs-v0_9 index) and handles batching, retries, and schema mapping for you. This endpoint does none of that — it relays bytes.

When to use this endpoint

  • You run a custom index whose schema you defined — audit events, billing records, app logs with your own field names. You control the wire format; the endpoint passes it through unchanged.
  • You already emit JSON and don’t want to adopt OpenTelemetry.
The trade-off is delivery: this endpoint is a thin proxy, so batching, retries, and back-pressure are yours to handle. The curl example below is a one-shot POST; the Vector example hands batching and retries to an agent. For OpenTelemetry-shaped logs going to otel-logs-v0_9, the OTLP endpoint is the better fit.

Prerequisites

  • A running Rootprint instance and its base URL — you’ll substitute it for <your-rootprint>.
  • A target index to write to: the built-in otel-logs-v0_9 (see Indexes for its schema) or a custom index you define first.
  • An ingest API key scoped to that index. In Settings → API keys, click Create ingest key, give it a name, and pick the target index. See API keys.
  • An HTTP client or log agent to send the request. The examples cover curl and Vector.

Setup

These examples write to the built-in otel-logs-v0_9 index, so they run with no schema setup. For your own index, keep the same mechanics and swap the record shape for your fields — see Shipping to a custom index.
1

Send records to the endpoint

Pick your client. curl posts a few lines by hand; Vector tails files and ships them continuously. Both write the same flat otel-logs-v0_9 records to /api/ingest/ndjson.
Replace <your-rootprint> and <your-ingest-token>. The payload is one JSON document per line: no trailing comma.
2

Verify in Rootprint

A successful POST returns {"num_docs_for_processing": <count>} — the number of documents Quickwit queued, not a guarantee they are searchable yet. Open Search, pick your index from the selector, and look for the records you just sent (or that Vector shipped). Allow a few seconds for Quickwit to commit.

Shipping to a custom index

The example above uses the built-in OTEL schema. Your own logs rarely look like that — and they don’t have to. The full workflow:
1

Define the index

Create an index with the fields your logs carry and one datetime field as the timestamp. Field types are text, i64, u64, f64, bool, datetime, ip, and json. See Create a custom index for the form and the field options. Then mint an ingest key scoped to it.
2

Send records that match the schema

POST NDJSON where each line is one record using your field names. For an audit-log index with fields ts, level, action, actor, and source_ip:
How unknown fields are treated depends on the index mode — dynamic stores them, lenient ignores them, strict rejects the record. Pick the mode when you create the index.
3

Map fields to display roles

Search needs to know which field is the severity, which is the message, and which holds a traceback. Set these on the index’s Configuration tab. The defaults assume the OTEL schema (severity_text, body.message), so point them at your fields — for the example above, level → level and message → action. These are presentation settings only; they never change the stored data.

Payload format

Rootprint forwards the body unchanged. Send NDJSON — one JSON document per line — with Content-Type: application/x-ndjson (the endpoint also accepts application/json, but the body must still be newline-delimited, not a JSON array). Each record must match the target index schema: see Indexes for the built-in otel-logs-v0_9 fields, or your index’s Configuration tab for the display mapping over a custom schema.

Troubleshooting

  • 401: missing or malformed Authorization: Bearer header.
  • 403: the API key is invalid or revoked. Mint a new ingest key for that index in Settings → API keys.
  • 400 with per-line parse errors: the response body names which documents failed. Common causes: a trailing comma, a JSON array instead of one object per line, or a value whose type doesn’t match the index schema.
  • 400 empty body: the request carried no body (or Content-Length: 0).
  • 413: a batch was too large for a reverse proxy in front of Rootprint, or the server’s body limit. Split it into smaller POSTs.
  • 503: Quickwit is unavailable, or the upload ran past the 120-second proxy timeout. Retry; if it persists, check that Quickwit is healthy.
  • Accepted but not visible in Search: a 200 means Quickwit queued the documents, not that they are committed. Wait a few seconds and search again.