BugShot

Docs

Set up BugShot

Two minutes from here to your first report. Everything below also lives on each project's page with your real key filled in.

Install

  1. Sign in and create a project. You get a project key.
  2. Paste the snippet before the closing </body> tag on every page.
  3. Press Send a test report on the project page, or open your site and tap the ladybug.
<script src="https://bugshot-cloud.thatfellowinc.workers.dev/bugshot.js"
        data-endpoint="https://bugshot-cloud.thatfellowinc.workers.dev/i/YOUR_PROJECT_KEY"
        defer></script>

Frameworks

The script tag works everywhere. If you would rather start it from your own code:

React or Next.js

// app/layout.tsx (Next.js App Router)
import Script from 'next/script'

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Script src="https://bugshot-cloud.thatfellowinc.workers.dev/bugshot.js"
          data-endpoint="https://bugshot-cloud.thatfellowinc.workers.dev/i/YOUR_PROJECT_KEY"
          strategy="afterInteractive" />
      </body>
    </html>
  )
}

Vue or Nuxt

// nuxt.config.ts
export default defineNuxtConfig({
  app: {
    head: {
      script: [{
        src: 'https://bugshot-cloud.thatfellowinc.workers.dev/bugshot.js',
        'data-endpoint': 'https://bugshot-cloud.thatfellowinc.workers.dev/i/YOUR_PROJECT_KEY',
        defer: true,
      }],
    },
  },
})

Any bundler

import { init, identify } from 'bugshot'

init({ endpoint: 'https://bugshot-cloud.thatfellowinc.workers.dev/i/YOUR_PROJECT_KEY' })
identify({ id: user.id, plan: user.plan })

Options

Set them as attributes on the script tag, or as camelCase keys passed to init().

AttributeDefaultWhat it does
data-endpointrequiredWhere reports are sent. Your project page shows the exact value.
data-projectdefaultA label sent with each report.
data-launcherbottom-rightbottom-right, bottom-left, top-right, top-left, or none to use your own button.
data-labelReport a bugText on the floating button.
data-accentladybug redYour brand colour for the send button and annotations, as #rgb or #rrggbb.
data-themeautolight, dark or auto (follows the visitor’s system).
data-hotkeyctrl+shift+bKeyboard shortcut. Set false to turn it off.
data-titleReport a problemHeading of the dialog.
data-placeholderPlaceholder in the description box.
data-require-emailfalseAsk the reporter for an email address.
data-mask-inputstrueBlur every field’s contents in the screenshot.
data-capture-consoletrueRecord console errors and warnings.
data-capture-networktrueRecord failed requests.
data-capture-breadcrumbstrueRecord clicks and page changes.

JavaScript API

BugShot.open()                                   // open the reporter yourself
BugShot.close()
BugShot.identify({ id: 'usr_88', plan: 'pro' })  // who is reporting
BugShot.setContext({ flags: ['new-checkout'] })  // app state for every report
BugShot.destroy()                                // remove it and restore patched globals

Use data-launcher="none" and call BugShot.open() from your own "Report a problem" menu item.

Privacy markup

<div data-bugshot-mask>Blurred in the screenshot</div>
<div data-bugshot-ignore>Left out of the screenshot entirely</div>

The classes bugshot-mask and bugshot-ignore work the same way.

Destinations

Every report is saved to your inbox. On a project's page you can also forward them:

  • Slack: an incoming webhook URL. Each new report posts with the screenshot and the technical details.
  • GitHub Issues: a repository and a fine-grained token with Issues read and write. A new bug opens one GitHub issue; if a resolved bug comes back, BugShot comments on it.
  • Linear: a personal API key and a team id. A new bug opens one Linear issue.
  • Webhook: any URL that accepts JSON, optionally signed.

Secrets are encrypted before they are stored and are never shown again.

Webhook payload

POST https://your-app.example/bugshot
content-type: application/json
x-bugshot-signature: sha256=<hex HMAC of the raw body, using your signing secret>

{
  "project": "Pantry",
  "issue": { "url": "...", "title": "...", "reportCount": 3 },
  "screenshotUrl": "https://.../s/...",
  "report": { "message": "...", "page": {...}, "env": {...},
              "console": [...], "network": [...], "breadcrumbs": [...] }
}

Verify the signature by computing an HMAC SHA-256 of the raw request body with your secret and comparing it to the header in constant time.

Agents and API

Let an AI agent work through your inbox. It reads a bug the way you do: the message, the page, the console errors, the failed requests, the click trail, the device and the screenshot. Then it can leave a note and mark the bug resolved. Agents connect over MCP, or over a plain REST API with a key.

1. Add BugShot to your agent

The MCP server uses Streamable HTTP at https://bugshot-cloud.thatfellowinc.workers.dev/mcp. Add that address and nothing else: there is no key to paste.

Run this in your terminal, then type /mcp in Claude Code, pick bugshot and sign in.

claude mcp add --transport http bugshot https://bugshot-cloud.thatfellowinc.workers.dev/mcp

Using something else? Any MCP client works with https://bugshot-cloud.thatfellowinc.workers.dev/mcp. Clients that cannot sign in use an API key from the Agents page.

2. Sign in when it asks

The first time the agent connects, your browser opens BugShot. A workspace owner picks the access and allows it:

  • Read only: the agent can list and read issues.
  • Read and write: the agent can also change an issue's status and add notes.

The connection shows under Connected apps on the Agents page, where you can disconnect it at any time. Sign in uses OAuth 2.1 with PKCE and dynamic client registration, which is what MCP clients expect.

Scripts, CI jobs and agents that cannot sign in use an API key instead. Owners create keys on the same page. A key is bsk_ followed by 40 hex characters, it is shown once, and it goes in the Authorization header as a Bearer token. Treat it like a password and keep it out of your repo.

3. Give it a first job

Paste this into your agent, from inside the repo of the app that sent the report:

Use BugShot to fix the newest open bug in this repo. Read the issue, find the cause, make the fix, then add a note with what you changed and mark it resolved.

Notes an agent writes show up on the issue page in your dashboard, so you can see what it found and what it changed.

What the agent can do

ToolWhat it doesNeeds
list_projectsList the projects in your account.Read only
list_issuesList issues. Arguments: status (open, resolved, ignored or all), project_id and limit.Read only
get_issueRead one issue as a Markdown brief: the message, the page, console errors, failed requests, the click trail and the device, plus the screenshot as an image (see the note below). Arguments: issue_id and include_screenshot.Read only
update_issue_statusSet an issue to open, resolved or ignored, with an optional note. Arguments: issue_id, status and note.Read and write
add_issue_noteAdd a note to an issue. Arguments: issue_id and body.Read and write

The server also offers a prompt, fix_bug. It takes an optional issue_id and defaults to the newest open issue.

The screenshot comes back as an image unless the reporter uploaded it themselves. Screenshots people upload are blurred in your dashboard until you open them, so they are not sent to the agent as an image. The rest of the brief still comes through.

REST API

Send an API key, or a token from signing in, as a Bearer token. Every response is JSON.

# list your open issues
curl -H "Authorization: Bearer bsk_your_key" \
  "https://bugshot-cloud.thatfellowinc.workers.dev/api/v1/issues?status=open&limit=20"

# mark one resolved (needs a read and write key)
curl -X POST -H "Authorization: Bearer bsk_your_key" \
  -H "content-type: application/json" \
  -d '{ "status": "resolved", "note": "Fixed in #482" }' \
  https://bugshot-cloud.thatfellowinc.workers.dev/api/v1/issues/ISSUE_ID/status
EndpointWhat it doesNeeds
GET /api/v1/projectsList your projects.Read only
GET /api/v1/issues?status=open&project=prj_...&limit=20List issues. Filter by status, project and limit.Read only
GET /api/v1/issues/{id}One issue with its latest reports, the Markdown brief and a screenshot URL. The screenshot comes with a sensitive flag.Read only
POST /api/v1/issues/{id}/statusBody: { "status": "resolved", "note": "Fixed in #482" }Read and write
POST /api/v1/issues/{id}/notesBody: { "body": "Investigated: ..." }Read and write

Errors are JSON like { "error": "..." }, with one of these status codes:

  • 401: the key or token is missing, wrong or expired.
  • 403: the key or connection is read only.
  • 404: the issue or project is not in your account.
  • 429: you are rate limited. Each key or connection gets 120 requests a minute.