Ingestion API reference
This is the platform-neutral HTTP API that every client integrates against. If you are building or maintaining a client package, implement against this contract. All routes are JSON over HTTPS.
Base path and versioning
The ingestion endpoints live under /api/v1. Send and accept application/json (the backup upload is the one multipart/form-data exception).
Authentication
Every request authenticates with a project API key (lm_...). Two header styles are accepted; the bearer form is preferred:
The key is matched by its SHA-256 hash and resolves to a single project. Authentication failures return 401:
Rate limits
- Ingestion (
/ingest,/logs,/dependencies,/heartbeats/*): 300 requests per minute, keyed by project token. - Backups (
/encryption-key,/backups*): 60 requests per minute.
Exceeding a limit returns 429 Too Many Requests.
When ingestion is suspended
While a payment is outstanding on the team's subscription, /ingest, /logs and /dependencies answer 402 Payment Required and store nothing:
Heartbeat pings keep being recorded during that window, so a suspended team's scheduled tasks do not start looking broken. Treat a 402 as a state to log and surface, not as an error to retry: it clears on its own when the payment goes through.
One vault endpoint answers 402 in the same window, with its own message: POST /backups. Reading stays open, the key material included, so a restore of what you have already paid for keeps working.
Listing, downloading and deleting a backup stay open, so an archive already stored can still be fetched. The key endpoint is the exception to notice: it also serves the material monitor:restore needs, so decrypting from the client waits for the payment while the blob itself does not.
POST /api/v1/ingest, report an exception
Reports a single exception occurrence.
Request body
Field rules
| Field | Required | Type / limit |
|---|---|---|
exception.class | yes | string, max 255 |
exception.message | no | string, max 8192 |
exception.code | no | any |
exception.file | no | string, max 1024 |
exception.line | no | integer ≥ 0 |
exception.trace | no | array |
context.environment | no | string, max 100 |
context.release | no | string, max 255 (hex SHA enables commit correlation) |
context.occurred_at | no | string, max 64 (parseable date) |
Responses
202 Accepted: stored:
``json { "message": "accepted", "stored": true, "issue_id": "01kw6tkrd8wjbfv2pktr7ssw43" } ``
202 Accepted: over quota, acknowledged but not stored:
``json { "message": "accepted", "stored": false } ``
422 Unprocessable Entity: validation failed.
issue_id is the issue's public identifier, a ULID. It is the same value the dashboard URL carries, so you can log it and paste it into the address bar. It is not a row number, and there is no event counter in the response: those would have told every project how many exceptions the whole platform had stored.
POST /api/v1/logs, report application logs (batch)
Stores a batch of log entries. Gated by the logs plan feature.
Request body
Field rules
| Field | Required | Type / limit |
|---|---|---|
logs | yes | array, 1–500 entries |
logs.*.level | no | string, max 20 |
logs.*.message | yes | string, max 8192 |
logs.*.context | no | array |
logs.*.channel | no | string, max 100 |
logs.*.environment | no | string, max 100 |
logs.*.release | no | string, max 255 |
logs.*.logged_at | no | string, max 64 |
Responses
202 Accepted:
``json { "message": "accepted", "stored": 137 } ``
stored is how many entries were actually saved (a partial batch is stored when the quota is nearly full).
403 Forbidden: plan does not include logs:
``json { "message": "Application logs are not available on your current plan." } ``
POST /api/v1/dependencies, report composer.lock
Replaces the project's dependency snapshot and triggers a background vulnerability scan.
Request body
Field rules
| Field | Required | Type / limit |
|---|---|---|
packages | yes | array, 1–5000 entries |
packages.*.name | yes | string, max 255 |
packages.*.version | yes | string, max 100 |
packages.*.is_dev | no | boolean |
Response
202 Accepted:
``json { "message": "accepted", "dependencies": 142 } ``
POST /api/v1/heartbeats/{slug}, ping a scheduled task
Signals that a scheduled task has just run. The request carries no body.
{slug} is normalised to lowercase letters, digits and dashes. The first ping on an unknown slug registers the heartbeat unarmed: it is recorded and never alerts until an expected period is set in the dashboard. See Heartbeats.
Responses
202 Accepted:
``json { "message": "pong", "armed": true } ``
armed says whether an expected period has been set, so a client can tell a recorded ping from a watched one.
422 Unprocessable Entity: the slug normalises to an empty string, or the project already holds the maximum number of heartbeats.
Backup vault endpoints
These power the encrypted backup vault. All require the backups plan feature. While a payment is outstanding, POST /backups answers 402 Payment Required; fetching the key material, listing, downloading and deleting stay open, so monitor:restore keeps working on the archives you already have.
GET /api/v1/encryption-key
Serves the team's key material so the client can seal and (with the passphrase, locally) unwrap.
403if the plan does not include backups.404if the team has not configured encryption keys.
GET /api/v1/backups
POST /api/v1/backups
multipart/form-data upload of an already-encrypted blob.
| Field | Required | Notes |
|---|---|---|
type | yes | database or files |
name | no | string, max 255 |
file | yes | the encrypted archive |
201 Createdreturns the stored backup's metadata (same shape as a list entry).403if the plan does not include backups.413 Request Entity Too Largeif the upload would exceed the storage quota:
``json { "message": "Backup storage quota exceeded.", "used_bytes": 5012345678, "limit_bytes": 5000000000 } ``
GET /api/v1/backups/{uuid}
Streams the encrypted blob as a download (<name>.enc).
DELETE /api/v1/backups/{uuid}
Deletes the backup. Returns 204 No Content.
Error summary
| Status | Meaning |
|---|---|
202 Accepted | Ingested (check stored to see whether/how much was kept). |
201 Created | Backup uploaded. |
204 No Content | Backup deleted. |
401 Unauthorized | Missing or invalid API key. |
402 Payment Required | Ingestion, key material and backup uploads suspended while a payment is outstanding. |
403 Forbidden | Feature not on the team's plan. |
404 Not Found | Encryption keys not configured (key endpoint). |
413 Payload Too Large | Backup storage quota exceeded. |
422 Unprocessable Entity | Validation failed. |
429 Too Many Requests | Rate limit exceeded. |
You are reading the Quiet Guard v1.0 documentation.