Source Maps & Symbols
Build/CI-time upload of symbolication inputs: R8/ProGuard mappings, source maps, Dart symbols, Apple dSYM archives and Android ELF objects. All uploads use a presign → PUT-to-storage → complete flow, so the file never transits this API and is not bound by request-size limits. Requires the symbols:upload scope; most integrators should use @newinstance/bugwatch-cli, which performs all three steps.
Artifact upload · step 1 – presign
Requests a presigned URL for a text symbolication artifact. This is step 1 of 2.
Scope: symbols:upload · Auth: project secret key (keyId:secret)
artifactType | File | Platform |
|---|---|---|
r8 / proguard | mapping.txt | android |
sourcemap | .map | ios · react-native · android |
dart-symbols | --split-debug-info output | flutter |
dsym | Apple dSYM referenced as an artifact | ios |
The file never passes through this API. You presign, PUT the bytes straight to storage, then confirm. That is why uploads are not bound by request-size limits; the previous raw-body routes were rejected by the edge proxy for any real symbol file.
1. POST …/presign → { uploadId, uploadUrl, expiresAt }
2. PUT <uploadUrl> → the raw bytes, streamed to storage
3. POST …/uploads/{uploadId}/complete → verified, then queuedThe API key goes on steps 1 and 3 only, never to the storage host, which is authorized by the presigned URL's own signature.
Uploads are idempotent per (release, platform, artifactType): completing a new
upload replaces the previous artifact and deletes the superseded file.
The release you declare must match the release your SDK reports at runtime, or the
artifact exists but nothing matches it and stacks stay unreadable.
Most integrators should use the CLI, which performs all three steps, streams the file (flat memory regardless of size) and retries transient failures:
# Binary debug-symbol archives - Apple dSYM, Android native .so
npx @newinstance/bugwatch-cli symbols upload MyApp.xcarchive \
--release "1.4.2" --build-number "318"
# Text artifacts - R8/ProGuard mapping, source map, Dart symbols
npx @newinstance/bugwatch-cli artifacts upload mapping.txt \
--release "1.4.2" --platform android --type r8The two commands are not interchangeable: symbols upload accepts binary archives
only and validates magic bytes, so a mapping.txt or .map is rejected.
Authorization
x-api-keyAPI key · headerrequiredRequest body
application/json{
"release": "1.0.0",
"platform": "android",
"artifactType": "r8",
"size": 128394,
"sha256": "3b8f2a91c0de4b7a8f13d6c25e09a4b1f87c30d2416e5a9b8c7d0e1f2a3b4c1d",
"originalName": "mapping.txt"
}Responses
201Presigned400Validation error401Invalid or missing API key403Missing symbols:uploadcurl -X POST 'https://api.newinstance.cloud/api/v1/bugwatch/artifacts/presign' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
--data-raw '{
"release": "1.0.0",
"platform": "android",
"artifactType": "r8",
"size": 128394,
"sha256": "3b8f2a91c0de4b7a8f13d6c25e09a4b1f87c30d2416e5a9b8c7d0e1f2a3b4c1d",
"originalName": "mapping.txt"
}'{
"uploadId": "665f1f77bcf86cd799439021",
"uploadUrl": "https://storage.newinstance.cloud/presigned/665f1f77bcf86cd799439021?sig=…",
"expiresAt": "2026-08-05T12:15:00.000Z"
}Artifact upload · step 2 – complete
Confirms the artifact landed in storage and makes it the active artifact for its (release, platform, artifactType). This is step 2 of 2.
Scope: symbols:upload
uploadId in the path is the id returned by step 1, the presign request.
What is verified
completeconfirms the object actually landed in storage and that its size matches what was declared at presign.- The pending row is looked up scoped to the calling project, so one project can never finalize another's upload (a miss returns
404). - Only an
AWAITING_UPLOADrow can be completed, so a completion cannot be replayed to re-queue processing. Completing it again answers200withalreadyCompleted: true. - For debug symbols the worker re-hashes the real bytes before indexing and rejects a mismatch (
CHECKSUM_MISMATCH). A declared checksum is never trusted on its own.
Authorization
x-api-keyAPI key · headerrequiredParameters
uploadIdpath · stringrequireddefault: Responses
200Upload already completed201Stored and active400Size mismatch401Invalid or missing API key404Upload not found409File never uploadedcurl -X POST 'https://api.newinstance.cloud/api/v1/bugwatch/artifacts/uploads/{uploadId}/complete' \
-H 'x-api-key: YOUR_API_KEY'{
"fileId": "f_9a2b1c3d4e5f",
"sha256": "3b8f2a91c0de4b7a8f13d6c25e09a4b1f87c30d2416e5a9b8c7d0e1f2a3b4c1d",
"alreadyCompleted": true
}Debug symbols · step 1 – presign
Requests a presigned URL for a binary debug-symbol archive. This is step 1 of 2.
Scope: symbols:upload · Auth: project secret key (keyId:secret)
| Platform | Accepted |
|---|---|
| ios · macos · tvos · watchos · visionos · catalyst | .zip of .dSYM bundles, .xcarchive dSYMs, or a raw Mach-O |
| android | Raw ELF .so / .debug |
The file never passes through this API. You presign, PUT the bytes straight to storage, then confirm. That is why uploads are not bound by request-size limits; the previous raw-body routes were rejected by the edge proxy for any real symbol file.
1. POST …/presign → { uploadId, uploadUrl, expiresAt }
2. PUT <uploadUrl> → the raw bytes, streamed to storage
3. POST …/uploads/{uploadId}/complete → verified, then queuedThe API key goes on steps 1 and 3 only, never to the storage host, which is authorized by the presigned URL's own signature.
Matching is by debug UUID / build-id, the identifier embedded in the binary, not by
release name. That is why an upload from any build machine resolves crashes from any device
running that exact binary, and why release here is metadata for search rather than the
match key. (Source maps and mappings are the opposite: those match on release + platform.)
Declaring a sha256 that already exists for this project short-circuits as a duplicate
and returns no uploadUrl, so the transfer is skipped entirely.
Most integrators should use the CLI, which performs all three steps, streams the file (flat memory regardless of size) and retries transient failures:
# Binary debug-symbol archives - Apple dSYM, Android native .so
npx @newinstance/bugwatch-cli symbols upload MyApp.xcarchive \
--release "1.4.2" --build-number "318"
# Text artifacts - R8/ProGuard mapping, source map, Dart symbols
npx @newinstance/bugwatch-cli artifacts upload mapping.txt \
--release "1.4.2" --platform android --type r8The two commands are not interchangeable: symbols upload accepts binary archives
only and validates magic bytes, so a mapping.txt or .map is rejected.
Authorization
x-api-keyAPI key · headerrequiredRequest body
application/json{
"platform": "ios",
"release": "1.0.0",
"buildNumber": "318",
"size": 48210944,
"sha256": "9c1e5b7a3f28d4c6b0a1e9f2d5c8b7a64310f2e7d9c1b5a8e3f60412789bcf02",
"originalName": "MyApp.app.dSYM.zip"
}Responses
200Archive already uploaded, transfer skipped201Presigned400Validation error401Invalid or missing API key403Missing symbols:uploadcurl -X POST 'https://api.newinstance.cloud/api/v1/bugwatch/debug-symbols/presign' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
--data-raw '{
"platform": "ios",
"release": "1.0.0",
"buildNumber": "318",
"size": 48210944,
"sha256": "9c1e5b7a3f28d4c6b0a1e9f2d5c8b7a64310f2e7d9c1b5a8e3f60412789bcf02",
"originalName": "MyApp.app.dSYM.zip"
}'{
"uploadId": "665f1f77bcf86cd799439011",
"status": "AVAILABLE",
"duplicate": true
}Debug symbols · step 2 – complete
Confirms the archive landed in storage and queues it for indexing. This is step 2 of 2.
Scope: symbols:upload
uploadId in the path is the id returned by step 1, the presign request. After this call, poll Get upload status for indexing progress.
What is verified
completeconfirms the object actually landed in storage and that its size matches what was declared at presign.- The pending row is looked up scoped to the calling project, so one project can never finalize another's upload (a miss returns
404). - Only an
AWAITING_UPLOADrow can be completed, so a completion cannot be replayed to re-queue processing. Completing it again answers200withalreadyCompleted: true. - For debug symbols the worker re-hashes the real bytes before indexing and rejects a mismatch (
CHECKSUM_MISMATCH). A declared checksum is never trusted on its own.
Archive-type validation happens in the worker, not here, because this API never sees
the bytes. An archive that is not a zip / Mach-O / ELF lands as INVALID with
UNRECOGNIZED_ARCHIVE; an ELF declared under an Apple platform fails with
ARCHIVE_PLATFORM_MISMATCH.
Authorization
x-api-keyAPI key · headerrequiredParameters
uploadIdpath · stringrequireddefault: Responses
200Queued for indexing400Size mismatch401Invalid or missing API key404Upload not found409File never uploadedcurl -X POST 'https://api.newinstance.cloud/api/v1/bugwatch/debug-symbols/uploads/{uploadId}/complete' \
-H 'x-api-key: YOUR_API_KEY'{
"uploadId": "665f1f77bcf86cd799439031",
"status": "QUEUED"
}List debug symbol uploads
Lists this project's recent debug symbol uploads, newest first.
Scope: symbols:read
Authorization
x-api-keyAPI key · headerrequiredParameters
limitquery · stringdefault: Optional. Number of uploads to return, 1 to 200. Defaults to 50.
Responses
200Uploads list401Invalid or missing API key403Missing symbols:readcurl -X GET 'https://api.newinstance.cloud/api/v1/bugwatch/debug-symbols/uploads?limit=20' \
-H 'x-api-key: YOUR_API_KEY'{
"uploads": [
{
"uploadId": "507f1f77bcf86cd799439011",
"status": "AVAILABLE",
"platform": "ios",
"release": "2.1.0",
"buildNumber": "1042",
"originalFilename": "MyApp.app.dSYM.zip",
"uploadedSize": 4823012,
"discoveredUuids": 3,
"validObjects": 3,
"invalidObjects": 0,
"createdAt": "2026-06-26T10:00:00.000Z",
"completedAt": "2026-06-26T10:00:15.000Z"
}
]
}Get upload status
Returns the processing status and indexing summary of one debug symbol upload.
Scope: symbols:read
uploadId in the path comes from the presign response.
Status flow: QUEUED, then EXTRACTING, then one of AVAILABLE, PARTIALLY_AVAILABLE (some objects were invalid), INVALID or FAILED.
Poll this until status is AVAILABLE or PARTIALLY_AVAILABLE before relying on symbolication.
Authorization
x-api-keyAPI key · headerrequiredParameters
uploadIdpath · stringrequireddefault: Responses
200Upload detail400Upload id is not valid401Invalid or missing API key404Upload not foundcurl -X GET 'https://api.newinstance.cloud/api/v1/bugwatch/debug-symbols/uploads/{uploadId}' \
-H 'x-api-key: YOUR_API_KEY'{
"uploadId": "507f1f77bcf86cd799439011",
"status": "AVAILABLE",
"platform": "ios",
"release": "2.1.0",
"buildNumber": "1042",
"discoveredDsyms": 2,
"discoveredUuids": 3,
"validObjects": 3,
"invalidObjects": 0,
"failureCode": null,
"failureMessage": null,
"createdAt": "2026-06-26T10:00:00.000Z",
"completedAt": "2026-06-26T10:00:15.000Z"
}Reprocess – re-symbolicate waiting crashes
Re-symbolicates crashes that arrived before this upload's symbols did.
Scope: symbols:reprocess
uploadId in the path is the debug symbol upload the waiting crashes need. Crashes still waiting for its symbols, or only partly symbolicated, are queued again and processed with them. Send an empty body.
Authorization
x-api-keyAPI key · headerrequiredParameters
uploadIdpath · stringrequireddefault: Responses
200Crashes queued for re-symbolication400Upload id is not valid401Invalid or missing API key403Missing symbols:reprocess404Upload not foundcurl -X POST 'https://api.newinstance.cloud/api/v1/bugwatch/debug-symbols/uploads/{uploadId}/reprocess' \
-H 'x-api-key: YOUR_API_KEY'{
"requeued": 12
}