Skip to content

Ingest API

The ingest API is the open, CDN-safe surface the JavaScript widget talks to from your users’ browsers. It is authenticated with your public project key (pr_…) in a header, uses no cookies, and has open CORS — the project key plus optional origin allowlisting is the security boundary.

This is a different API from the REST API: that one lives under /api/v1, is authenticated with a secret Bearer key (pr_sk_…), and is for server-side reads and admin. The ingest API is for the widget only. Most people never call it directly — the widget does. It is documented here for custom clients and debugging.

https://room.patchrooms.com/ingest

Every request carries the public project key in a header:

x-alphabug-project-key: pr_xxx

If a project sets allowedOrigins, a browser request whose Origin is not on that list is rejected. Same-origin requests that omit Origin (e.g. first-party GETs) pass. An identity token may be attached with x-alphabug-identity: <token> for verified user identity; it is validated against the project’s identity secret and ignored if unset.

Auth failure returns 401 { "error": "invalid_project_key_or_origin" } on every route (except GET /flags/:key, which returns { "enabled": false }).

Returns the widget configuration and any active channel hints. Used by the programmatic loader to theme itself.

{
"widgetConfig": { "mascot": "fox", "anchor": "middle-right" },
"hints": [{ "channelKey": "bug", "weight": "max", "userId": null }]
}

Returns the project’s non-archived channels (sorted oldest first) and the default channel key.

{
"defaultChannelKey": "bug",
"channels": [
{
"id": "665d...",
"projectId": "665e...",
"key": "bug",
"name": "Bug",
"color": "#c0392b",
"mascot": "fox",
"baseWeight": 1,
"matchers": [{ "conditions": [], "weight": 0, "note": "" }],
"archived": false,
"createdAt": "2026-05-01T12:00:00.000Z",
"updatedAt": "2026-05-01T12:00:00.000Z"
}
]
}

Checks whether an alpha flag is enabled for the project. Returns { "enabled": false } for unknown flags or auth failure.

{ "enabled": true }

Uploads a screenshot or audio blob before it is referenced from a report. The blob is stored and returned as a blobId; it stays unbound until a report references it. multipart/form-data:

FieldTypeDescription
filefileThe binary. Required.
kind'screenshot' | 'audio'Required. Selects the size/mime limits.
width, heightnumberScreenshot pixel dimensions (optional).
durationMsnumberAudio duration (optional, capped at the audio block limit).
{ "blobId": "665f...", "mime": "image/png", "bytes": 20481 }

Errors: 400 kind_invalid, 400 file_required, 400 duration_too_long, 413 block_too_large, 415 mime_not_allowed. Server-stored mime/bytes/ dimensions are authoritative — a report’s block metadata is overwritten with them.

Submits a report. application/json:

FieldTypeDescription
blocksBlock[]Required. The report payload (text / screenshot / audio / selection). Binary blocks reference a blobId from POST /blob.
channelKeystringOptional. Which channel to file into — see resolution below.
contextobjectOptional. Page context, plus artifactId / artifact for Rooms.
{ "reportId": "665f..." }

channelKey is resolved, never trusted verbatim. The server looks for a real, non-archived channel with that key; if it doesn’t exist, it silently falls back to the project’s defaultChannelKey; if that doesn’t resolve either, the request is rejected with 400 channel_invalid. An unknown channel is never auto-created — channels are created only through the REST API or the dashboard. See Channels.

HTTPerrorMeaning
400blocks_invalidBlocks failed schema validation.
400blob_id_invalidA referenced blobId is not a valid id.
400channel_invalidNo requested/default channel resolves.
400context_invalidcontext failed schema validation.
400blob_mime_mismatchA blob’s type doesn’t match the block referencing it.
403blob_project_mismatchA referenced blob belongs to another project.
404blob_not_foundA referenced blob does not exist.
409blob_already_usedA referenced blob is already bound to a report.