Home

BugTape docs

Ingest Payload Schema

POST /v1/ingest fields and receipts.

Detailed specification for the POST /v1/ingest request and response.

Request schema

Capture identity and retry safety

The SDK sends schemaVersion: 1 and a UUID captureId generated once per captured report. Keep the entire accepted payload unchanged across retries. An identical retry in the authenticated project returns the original acceptance, including occurrenceId, without creating another occurrence, event or charge. The response includes projectId derived from the current authenticated project, including when replaying older receipts that did not store that field. A changed payload with that ID returns409 CAPTURE_ID_CONFLICT. Fresh API-key checks still apply. Legacy clients without an ID retain their previous submission behavior.

Optional fields: capturedAt (ISO8601 with timezone), sdkName, sdkVersion, captureSource, osFamily, framework, runtime. The server adds receivedAt. applicationId, buildId and installationId must refer to server registry records in the key's project; build/install IDs also require the matching application. These labels do not grant CI/agent authority. Registry management routes are not yet released.

Top-level fields

FieldTypeRequiredMax sizeDefaultDescription
titlestringYes500 charsBug report title
descriptionstringNo10,000 chars""Detailed description
severityenumNo"medium""critical", "high", "medium", "low"
urlstringNo2,000 charsPage URL where the bug occurred
userAgentstringNo500 charsBrowser user agent string
viewportstringNo50 charsViewport dimensions, e.g. "1920x1080"
eventsEvent[]No1,000 items[]Captured browser events
screenshotanyNonullLegacy field; ignored. Occurrence records not_stored. Never a ready file
audioanyNonullLegacy field; ignored. Occurrence records not_stored. Never a ready file
aiSummaryobjectNonullAI-generated analysis
encryptedobjectNoUnsupported end-to-end; do not send secrets here
reporterEmailstringNovalid emailReporter email for attribution
reporterIdstringNo200 charsApp-specific reporter identifier
sessionIdstringNo200 charsSession identifier for occurrence grouping
userIdstringNo200 charsEnd-user or account identifier
releasestringNo200 charsRelease, build, or deploy version (app version on mobile). Omitted release is a structured miss (release_missing), never HTTP 400
stackstringNo10,000 charsOptional stack for server / non-browser captures. Stored on occurrence metadata.stack and as an error event when events omit stack. Omitted stack is a structured miss (stack_missing), never HTTP 400
framesobject[]No200 itemsOptional parsed frames; stored on occurrence metadata.frames when present
mcp_timelineobject[]No500 itemsOptional MCP timeline retained on occurrence metadata.mcp_timeline and kept when a failure pack is stored without its own timeline. Ingest does not create packs
environmentstringNo100 charsEnvironment label such as production or staging
platformstringNo40 charsinferredCapture platform: web, ios, android, react-native, flutter, server, other. Aliases (iPhone, node, expo, …) are normalised. Inferred from userAgent when omitted (browser UA → web, CFNetwork → ios, no UA → server). On the v1 web/server path, platform is not part of the fingerprint — one issue can span platforms. Native v2 fingerprints hash a canonical JSON blob that includes platform plus application/build/site
devicestringNo120 charsDevice model, e.g. iPhone15,3 (stored in occurrence metadata.device)
osVersionstringNo60 charsOS version, e.g. 17.4.1 (stored in occurrence metadata.osVersion)
metadataobjectNo{}Additional occurrence metadata for live feed and triage

Event schema

Each event in the events array:

FieldTypeRequiredDefaultDescription
typestringYes"unknown"Event type identifier
timestampnumberNonowUnix timestamp in milliseconds
dataanyNoEvent-specific payload

Common event types and their data shapes

Error events

// type: "error"
{
  message: string;     // Error message
  filename: string;    // Source file URL
  lineno: number;      // Line number
  colno: number;       // Column number
  stack: string;       // Full stack trace
}

// type: "error:unhandledrejection"
{
  message: string;
  stack: string;
}

Console events

// type: "console:log" | "console:warn" | "console:error" | "console:info" | "console:debug"
{
  level: string;
  args: string[];      // Serialized arguments (max 2,000 chars each)
}

Network events

// type: "network" (resource timing)
{
  url: string;
  duration: number;    // milliseconds
  transferSize: number;
  initiatorType: string;
}

// type: "network:fetch"
{
  url: string;
  method: string;      // GET, POST, etc.
  status: number;      // HTTP status code
  duration: number;
}

// type: "network:fetch:error"
{
  url: string;
  method: string;
  error: string;
  duration: number;
}

// type: "network:xhr"
{
  url: string;
  method: string;
  status: number;
  duration: number;
}

Rage click events

// type: "rage-click"
{
  x: number;
  y: number;
  clicks: number;
  target: string;      // CSS selector or tag name
  url: string;
  innerText: string;
}

DOM events

// type: "dom"
// rrweb event objects — see https://github.com/rrweb-io/rrweb
// These are opaque to the API and stored as-is.

Evidence and encryption limits

This intake stores structured report fields and events. It does not persist the legacy screenshot, audio or encrypted fields as retrievable artifacts. Offered values are dropped and recorded on the occurrence as metadata.media.<field> = { stored: false, reason: "not_stored" }. is_encrypted is never set from a payload flag — ciphertext and key ownership do not exist on this path. A 201 receipt includes media: { ready: false, stored: false, notStored: [...] } and is not proof of media storage. The SDK sends approved PNG bytes through the separate evidence reservation/upload lifecycle only when capability discovery admits PNG. That path has local end-to-end qualification; hosted media storage remains unavailable (GET /v1/evidence/capabilities stays storage_unavailable until G9 is MET). Report and image retry identities stay fixed, and SDK success waits for a ready artifact. See the SDK README for storage limits. Client encryptionKey is rejected by the SDK because end-to-end ciphertext storage/read/decryption is not supported.

Response schema

Success (201 Created)

{
  id: string;              // UUID — stable report identifier
  projectId: string;       // UUID derived from the authenticated capture key
  occurrenceId: string;    // UUID of the accepted occurrence
  schemaVersion?: 1;       // Present with capture identity
  captureId?: string;      // Echoes the accepted capture UUID
  receivedAt?: string;     // Original server acceptance time
  fingerprint: string;     // 16-char hex — dedup key
  deduplicated: boolean;   // true if grouped with existing open bug
  regression: boolean;     // true if reopened a resolved bug
  is_regression: boolean;  // alias for regression
  media: {                 // Ingest never stores screenshot/audio/encrypted
    ready: false;
    stored: false;
    notStored: Array<'screenshot' | 'audio' | 'encrypted'>;
  };
}

Error responses

400 — Validation failed

{
  "error": "Validation failed",
  "details": [
    { "code": "too_small", "minimum": 1, "type": "string", "inclusive": true, "exact": false, "message": "String must contain at least 1 character(s)", "path": ["title"] }
  ]
}

400 — Malformed JSON

{ "error": "Invalid JSON in request body" }

401 — Auth failure

{ "error": "Missing or invalid API key" }

402 — Plan limit exceeded

{
  "error": "Plan limit reached",
  "limit": 100,
  "current": 100,
  "plan": "free",
  "upgradeUrl": "/console/#settings"
}

429 — Rate limited

{
  "error": "Too many requests",
  "retryAfter": 45
}

Field size and truncation guidance

FieldMax sizeTruncation behavior
title500 charsTruncated to 500 characters
description10,000 charsTruncated to 10,000 characters
url2,000 charsTruncated to 2,000 characters
userAgent500 charsRejected if > 500
viewport50 charsRejected if > 50
events1,000 itemsFirst 1,000 accepted event objects retained
Event dataNo hard limitStored as JSONB; keep individual event data under 100KB
Total body10 MBRequest rejected if body exceeds limit
Console args2,000 chars/argTruncated by SDK at capture time

Recommendation: If you buffer more than 1,000 events, prioritize errors, network failures, and console errors. Drop DOM mutation events first, as they are the most voluminous.

Deduplication behavior

Reports are deduplicated by fingerprint. Web/server (v1) uses:

SHA256(errorType + ":" + normalizedMessage + ":" + urlPattern).slice(0, 16)

Native (ios / android / react-native / flutter) uses v2: SHA256 of a canonical JSON failureSignature that includes version, platform, applicationId, buildId, signal, and the symbol site or image offset. That formula is different from v1. Do not send a native crash expecting it to merge with a web report of the same title.

Normalization rules:

  • Hex addresses (0xABCD) → <hex>
  • Timestamps (13-digit numbers) → <timestamp>
  • UUIDs → <uuid>
  • IP addresses → <ip>
  • Port numbers → :<port>
  • Large numbers (8+ digits) → <num>
  • URL path segments with numeric IDs → <id>

Matching behavior:

  • Same fingerprint + open bug → increments report_count (response: deduplicated: true)
  • Same fingerprint + resolved bug → reopens the bug (response: regression: true)
  • New fingerprint → creates a new bug (response: deduplicated: false, regression: false)

Plan limits

PlanBugs per monthProjectsIntegrationsTeam members
Free100113
Pro1,00051010
Team5,000205050
EnterpriseUnlimitedUnlimitedUnlimitedUnlimited

When the monthly bug limit is reached, the API returns 402 Payment Required.