Documentation

Ingest API

POST /v1/logs JSON shape, auth headers, log levels, and response codes.

Send logs to the API with a single JSON POST. Same contract for curl, the CLI, and the Serilog sink.

Endpoint

POST /v1/logs
Content-Type: application/json
EnvironmentBase URL
Local Dockerhttp://localhost:5080
Hostedhttps://api.qiklog.com

Authentication

Production requires a key on every ingest. Local Docker defaults to keys optional. See API keys.

Send the key with one of these headers:

HeaderExample
Authorization: BearerAuthorization: Bearer ql_your_full_key_here (recommended)
X-QikLog-API-KeyX-QikLog-API-Key: ql_your_full_key_here
X-Api-KeyX-Api-Key: ql_your_full_key_here (legacy alias)

CLI and Serilog send Authorization: Bearer. The landing tape uses the same hosted curl.

Hosted curl

export QIKLOG_API_KEY='ql_your_full_key_here'

curl -X POST https://api.qiklog.com/v1/logs \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $QIKLOG_API_KEY" \
  -d '{"source":"demo","level":"info","message":"hello from curl"}'

Use source demo to see the line on the live landing panel and at /tail/demo on the dashboard.

Equivalent with the product header:

curl -X POST https://api.qiklog.com/v1/logs \
  -H "Content-Type: application/json" \
  -H "X-QikLog-API-Key: $QIKLOG_API_KEY" \
  -d '{"source":"demo","level":"info","message":"hello from curl"}'

A successful ingest returns HTTP 202 Accepted with an empty body.

Local curl

curl -X POST http://localhost:5080/v1/logs \
  -H "Content-Type: application/json" \
  -d '{"source":"demo","level":"info","message":"payment captured"}'

When local auth is on, add the same Bearer or X-QikLog-API-Key header as production.

Request body

FieldRequiredTypeDescription
sourceYesstringLogical source name (trimmed). Use stable names per app or environment.
messageYesstringLog line body (plain text).
levelNostring or intDefault info. See levels below.
timestampNoISO 8601Event time (UTC). Defaults to server receipt time.
propertiesNoobjectFlat key/value strings for structured metadata.

Example

{
  "source": "checkout-api",
  "level": "warning",
  "message": "payment retry attempt 2",
  "timestamp": "2026-06-01T12:00:00Z",
  "properties": {
    "orderId": "ord_123",
    "region": "us-east"
  }
}

Log levels

Accepted as case-insensitive strings or integers:

StringIntUse for
trace0Verbose tracing
debug1Debug detail
info2Normal operations
warning or warn3Recoverable issues
error or err4Failures
critical or crit5Severe / page-worthy

Responses

StatusMeaning
202 AcceptedLog accepted, broadcast to live subscribers, queued for storage
400 Bad RequestMissing source/message, invalid JSON, or unknown level
401 UnauthorizedMissing API key (when auth is enabled)
403 ForbiddenInvalid or revoked API key
429 Too Many RequestsPer-key rate limit exceeded (default 120/minute)

What happens after ingest

  1. The API validates your payload.
  2. If auth is enabled, your API key is verified and rate-limited.
  3. The entry is saved to Postgres (log_entries).
  4. SignalR pushes the line to everyone viewing tail/{source} in the dashboard.

Health check

curl https://api.qiklog.com/healthz

Local: curl http://localhost:5080/healthz.

Returns status and postgres connectivity when a database is configured.

Next steps

  • Live tail: watch lines stream in the browser
  • API keys: mint and send a Bearer key
  • Serilog: one-line .NET sink
  • CLI: qiklog send from a terminal