3 · BugWatch — Browser & Mobile
/api/v1/bugwatch/ingest/browserBrowser ingest – error event
Ingest events from browser / SPA applications.
Authentication: x-bugwatch-session — a short-lived token minted by your backend via POST /api/v1/bugwatch/browser-session. Run "Mint browser session token" first; the test script saves it as {{sessionToken}} automatically.
The secret key never reaches the browser. The session token is ingest-only, scoped to one project+environment, and honours the project's allowedOrigins allow-list.
Request
- Header:
x-bugwatch-session: {{sessionToken}} - Content-Type:
application/jsonorapplication/x-ndjson - Max body: 2 MB
- CORS: allowed from any origin in the project's
allowedOrigins(empty = allow all)
Success — 202 Accepted
{ "ingested": 1, "skipped": 0, "deduped": 0 }Common errors
401— invalid or expired session token403— origin not inallowedOrigins(origin_not_allowed)404— project not found or inactive
Headers
x-bugwatch-sessionRequest body
application/json{
"level": 50,
"time": {{nowMs}},
"message": "Uncaught TypeError in browser",
"release": "1.0.0",
"environment": "production",
"tags": { "browser": "Chrome", "page": "/checkout" },
"user": { "id": "usr_browser_001", "ip": "203.0.113.42" },
"exception": {
"type": "TypeError",
"value": "Cannot read properties of null (reading 'getAttribute')",
"stacktrace": {
"frames": [
{ "filename": "https://app.example.com/static/js/main.chunk.js", "function": "handleClick", "lineno": 112, "colno": 45 }
]
}
}
}Responses
202 – Browser event ingested
{
"ingested": 1,
"skipped": 0,
"deduped": 0
}http://localhost:5050/api/v1/bugwatch/ingest/browserHeaders
x-bugwatch-sessionRequest body
Code samples
curl -X POST 'http://localhost:5050/api/v1/bugwatch/ingest/browser' \
-H 'Content-Type: application/json' \
--data-raw '{
"level": 50,
"time": {{nowMs}},
"message": "Uncaught TypeError in browser",
"release": "1.0.0",
"environment": "production",
"tags": { "browser": "Chrome", "page": "/checkout" },
"user": { "id": "usr_browser_001", "ip": "203.0.113.42" },
"exception": {
"type": "TypeError",
"value": "Cannot read properties of null (reading '\''getAttribute'\'')",
"stacktrace": {
"frames": [
{ "filename": "https://app.example.com/static/js/main.chunk.js", "function": "handleClick", "lineno": 112, "colno": 45 }
]
}
}
}'/api/v1/bugwatch/ingest/mobileMobile ingest – iOS native crash
Ingest events from native mobile apps (iOS, Android, Flutter, React Native).
Authentication: x-bugwatch-token — a client-signed token built on device by the SDK. The app embeds a per-project mobileAppSecret (HKDF-derived), signs each request locally, and sends only the signed token. The secret is never transmitted.
The pre-request script on this request reproduces the exact token the SDK builds using CryptoJS. Set projectId, environment, and mobileAppSecret in your environment before running.
Token format:
base64url(claims).base64url(HMAC-SHA256(claims, appSecret))
Claims (deterministic key order, no spaces):
{"pid":"<projectId>","env":"<env>","iat":<nowSec>,"exp":<nowSec+300>,"nonce":"<16 hex chars>"}Request
- Header:
x-bugwatch-token: {{mobileToken}}(auto-built by pre-request script) - Content-Type:
application/jsonorapplication/x-ndjson - Max body: 2 MB
- Rate limit: 12,000 events / 60 seconds per project
Success — 202 Accepted
{ "ingested": 1, "skipped": 0, "deduped": 0 }Common errors
401— invalid/expired/malformed token429— rate limited
Event model (what each SDK puts on the wire)
One merged event per request (eventId, time, level required). The four native SDKs share this endpoint:
- Native crashes (iOS, Android NDK) carry
payloadVersion: 2with top-levelbinaryImages+nativeStacktrace(iOS) /nativeFrames(Android) — rawinstruction_addrvalues the worker symbolicates against the uploaded dSYM / ELF by UUID / build-id. - JVM / Dart / JS exceptions (Android, Flutter, React Native) and handled errors carry
exception.stacktraceas an array of{ filename, function, lineno, colno, in_app }frames — de-obfuscated server-side via the uploaded R8 mapping / dart-symbols / source map. - All SDKs also emit ANR / app-hang, message, and release-health session events through this same endpoint.
See the per-platform examples in this folder — iOS native crash, Android JVM, Android NDK, Flutter, React Native — and the matching upload in 4 · Source Maps & Symbols.
Headers
x-bugwatch-tokenRequest body
application/json{
"eventId": "bw_e_{{$guid}}",
"time": {{nowMs}},
"level": 60,
"message": "Fatal: EXC_BAD_ACCESS (SIGSEGV)",
"platform": "ios",
"release": "1.0.0",
"environment": "production",
"sdk": {
"name": "bugwatch-ios",
"version": "0.1.1"
},
"device": {
"model": "iPhone15,2",
"family": "iPhone",
"osName": "iOS",
"osVersion": "17.4.1",
"bundleId": "com.example.MyApp",
"appVersion": "1.4.2",
"appBuild": "318"
},
"exception": {
"type": "EXC_BAD_ACCESS",
"value": "Attempted to dereference a null pointer",
"stacktrace": null
},
"binaryImages": [
{
"name": "MyApp",
"debug_id": "550e8400-e29b-41d4-a716-446655440001",
"arch": "arm64",
"image_addr": "0x100008000",
"image_size": 65536,
"is_main_image": true
},
{
"name": "Foundation",
"debug_id": "7c8f1a22-9b33-4d55-8e21-0a1b2c3d4e5f",
"arch": "arm64",
"image_addr": "0x1db000000",
"image_size": 2097152
}
],
"nativeStacktrace": [
{
"frame_index": 0,
"instruction_addr": "0x100008234",
"image_addr": "0x100008000",
"image_name": "MyApp",
"in_app": true
},
{
"frame_index": 1,
"instruction_addr": "0x1db123456",
"image_addr": "0x1db000000",
"image_name": "Foundation"
}
],
"crashedThreadId": 0,
"payloadVersion": 2,
"tags": {
"device": "iPhone 15 Pro"
},
"user": {
"id": "usr_ios_001"
}
}Responses
202 – Mobile event ingested
{
"ingested": 1,
"skipped": 0,
"deduped": 0
}http://localhost:5050/api/v1/bugwatch/ingest/mobileHeaders
x-bugwatch-tokenRequest body
Code samples
curl -X POST 'http://localhost:5050/api/v1/bugwatch/ingest/mobile' \
-H 'Content-Type: application/json' \
--data-raw '{
"eventId": "bw_e_{{$guid}}",
"time": {{nowMs}},
"level": 60,
"message": "Fatal: EXC_BAD_ACCESS (SIGSEGV)",
"platform": "ios",
"release": "1.0.0",
"environment": "production",
"sdk": {
"name": "bugwatch-ios",
"version": "0.1.1"
},
"device": {
"model": "iPhone15,2",
"family": "iPhone",
"osName": "iOS",
"osVersion": "17.4.1",
"bundleId": "com.example.MyApp",
"appVersion": "1.4.2",
"appBuild": "318"
},
"exception": {
"type": "EXC_BAD_ACCESS",
"value": "Attempted to dereference a null pointer",
"stacktrace": null
},
"binaryImages": [
{
"name": "MyApp",
"debug_id": "550e8400-e29b-41d4-a716-446655440001",
"arch": "arm64",
"image_addr": "0x100008000",
"image_size": 65536,
"is_main_image": true
},
{
"name": "Foundation",
"debug_id": "7c8f1a22-9b33-4d55-8e21-0a1b2c3d4e5f",
"arch": "arm64",
"image_addr": "0x1db000000",
"image_size": 2097152
}
],
"nativeStacktrace": [
{
"frame_index": 0,
"instruction_addr": "0x100008234",
"image_addr": "0x100008000",
"image_name": "MyApp",
"in_app": true
},
{
"frame_index": 1,
"instruction_addr": "0x1db123456",
"image_addr": "0x1db000000",
"image_name": "Foundation"
}
],
"crashedThreadId": 0,
"payloadVersion": 2,
"tags": {
"device": "iPhone 15 Pro"
},
"user": {
"id": "usr_ios_001"
}
}'