API Reference
The Lumber REST API base URL is https://api.lumber.infinitetoken.com.
Most developers should use the Node.js SDK rather than calling the API directly. The SDK handles encryption, queuing, retries, and authentication automatically. The only endpoint documented for direct use is log ingestion. The rest of the API serves the Lumber app.
Authentication
Log ingestion is authenticated with an app API key in the Authorization header:
Authorization: Bearer your_api_key
API keys are created in the Lumber app on your app's Secrets screen. See Authentication.
Log Ingestion
POST /logs
Ingest a single encrypted log entry. Send one request per log - there is no batch format. Authenticated with an app API key. Send the body as JSON with a Content-Type: application/json header.
Request body:
{
"level": 2,
"encrypted": {
"nonce": "base64encodedIV",
"payload": "base64encodedAuthTagAndCiphertext",
"keyVersion": 1
}
}
| Field | Type | Description |
|---|---|---|
level | number | Log level (see below). Stored unencrypted. |
encrypted.nonce | string | Base64 of the 12-byte AES-GCM IV |
encrypted.payload | string | Base64 of the 16-byte GCM auth tag followed by the ciphertext |
encrypted.keyVersion | number | Key version. The SDK always sends 1. |
The plaintext is the JSON-encoded log payload (level, message, and optional requestId, traceId, context, and metadata), encrypted with AES-256-GCM using the app's 32-byte encryption key.
Log levels:
| Value | Name |
|---|---|
0 | debug |
1 | info (default for lumber.log()) |
2 | warn |
3 | error |
4 | fatal |
Response: 201 Created with the stored record:
{
"data": {
"id": "6f1c2a9e-...",
"appId": "0b7d4e12-...",
"level": 2,
"encrypted": {
"nonce": "base64encodedIV",
"payload": "base64encodedAuthTagAndCiphertext",
"keyVersion": 1
},
"createdAt": "2026-10-15T14:32:01.452Z"
},
"metadata": {
"requestId": "a3e1c7d0-...",
"durationMs": 12
}
}
Error responses:
| Status | Description |
|---|---|
400 | Malformed JSON, or a missing or invalid field (level, encrypted, encrypted.nonce, encrypted.payload, encrypted.keyVersion) |
401 | Missing Authorization header, or an invalid or deleted API key |
413 | Request body larger than 100 KB |
500 | Server error |
Response format
Successful responses use a consistent envelope:
{
"data": { ... },
"metadata": {
"requestId": "string",
"durationMs": 0
}
}
requestId echoes the X-Request-Id request header when you send one, and is a generated UUID otherwise.
Error responses use standard HTTP status codes with a JSON body containing a message:
{
"message": "Invalid token"
}
Server errors (5xx) always return { "message": "Internal Server Error" }.