# Source maps & symbolication

> **For agents:** uploading is a binary flow with no MCP tool — tell the human to run `sentry-cli` as shown below with a `release:write` token. You can verify the result: `list_releases(org="acme")` shows `artifactBundles` per release, and `get_fix_context` frames carry `file`/`line`/`context` once maps resolve (`event.symbolication.partial` with `reasons` flags frames that did not).

Minified JavaScript frames are useless until the processor can find the matching source map. Bugwatch resolves maps in two ways, tried in order:

1. **Debug ID** — the `debugId` injected into the bundle and its map by modern build plugins (`withSentryConfig`, `@sentry/vite-plugin`, `sentry-cli sourcemaps inject`). Exact and release-independent.
2. **Release + dist + URL path** — for maps uploaded without debug IDs, the frame's `release`, the artifact `dist`, and the script URL path (`/assets/app.js` → `/assets/app.js.map`) look up the bundle uploaded under that release.

Set `release` in the SDK to the same string you upload under, or only debug-ID resolution can work.

## Upload with sentry-cli (recommended)

The API implements the `sentry-cli` chunk-upload protocol, so existing CI steps work by pointing `--url` at Bugwatch:

```sh
export SENTRY_AUTH_TOKEN=bwt_…   # needs release:write
sentry-cli --url https://api.bugwatch.io sourcemaps inject ./dist
sentry-cli --url https://api.bugwatch.io sourcemaps upload \
  --org acme --project web --release 1.4.2 ./dist
```

Framework plugins take the same URL as `sentryUrl` / `url` (Next.js `withSentryConfig`, Nuxt `sourceMapsUploadOptions`, the Vite/webpack plugins). Native symbol uploads for mobile (`debug-files upload`) use the same token and URL.

What happens underneath, for debugging:

| Step | Route | Notes |
|---|---|---|
| Options | `GET /api/0/organizations/{org}/chunk-upload/` | Advertises 8 MiB chunks, SHA-1 names, gzip, up to 64 chunks (32 MiB) per request, `accept: ["artifact_bundles"]` only |
| Chunks | `POST /api/0/organizations/{org}/chunk-upload/` | multipart parts named by the SHA-1 of the raw chunk; checksums verified |
| Assemble | `POST /api/0/organizations/{org}/artifactbundle/assemble/` | `{checksum, chunks[], projects[], version?, dist?}` → `{state: "ok" \| "not_found" \| "error", missingChunks[]}`; assembled synchronously; the release is created if missing |

Legacy release-file uploads are not advertised; only artifact bundles (a zip with `manifest.json` + `files/`) are accepted.

## Native endpoint (no CLI)

`POST /v1/orgs/{org}/projects/{project}/artifact-bundles` with `release:write`:

```json
{
  "release": "1.4.2",
  "dist": "web",
  "files": [
    { "name": "~/assets/app.js.map", "debugId": "3f2a1c4e-…", "content": "{\"version\":3,…}" }
  ]
}
```

- Up to 500 files, 25 MB per file, 200 MB per bundle (JSON string content).
- `release`, `dist`, and `debugId` are optional, but the release **must already exist** (`create_release` or `POST /v1/orgs/{org}/releases`), otherwise `404`.
- Names are normalised to a `/`-rooted path: `~/assets/x.js.map`, `https://cdn/assets/x.js.map`, and `assets/x.js.map` all become `/assets/x.js.map`.

Both paths store files in R2 under `artifacts/{project}/{sha256}`, index them in the control database, and warm the lookup manifests the processor reads (`artifacts:{project}:{debugId}` and `relmap:{project}:{release}:{dist}`).

## Verifying

- `list_releases` → `artifactBundles` should be ≥ 1 for the release.
- Send a test error from the built bundle and open the issue: in-app frames should show file, line, and three lines of context on each side.
- The processed event lists any frames it could not resolve under `symbolication_failures` (surfaced as `event.symbolication` in fix context). Common causes: `release` mismatch between SDK and upload, maps uploaded without `.map` suffix mapping, or a `dist` set on upload but not in the SDK.

## Beyond JavaScript

Mobile symbol files (dSYM, ProGuard/R8, Flutter split-debug-info) go through the same `sentry-cli` URL and token; see [Mobile & desktop](https://docs.bugwatch.io/sdks/mobile-desktop.md). Server-side languages report symbolic frames and need nothing.
