Smushbug

Docs

Set up Smushbug

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://smushbug.com/smushbug.js"
        data-endpoint="https://smushbug.com/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://smushbug.com/smushbug.js"
          data-endpoint="https://smushbug.com/i/YOUR_PROJECT_KEY"
          strategy="afterInteractive" />
      </body>
    </html>
  )
}

Vue or Nuxt

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

Any bundler

import { init, identify } from 'bugshot'

init({ endpoint: 'https://smushbug.com/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-rightWhere the launcher sits: bottom-right, bottom-left, top-right or top-left. The tab goes on that side of the screen. Use none to open it from your own button.
data-launcher-styletabtab is a slim tab on the screen edge, button is the round button. Leave it off and the Launcher setting on your project page decides.
data-nudge-on-errortrueWiggle the tab once and show a small dot when the page first logs an error. The tab never opens by itself.
data-labelReport a bugName of the launcher: shown on the round button and when the tab is pointed at, and read out by screen readers.
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

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

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

Privacy markup

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

The classes smushbug-mask and smushbug-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, Smushbug 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/smushbug
content-type: application/json
x-smushbug-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 Smushbug to your agent

The MCP server uses Streamable HTTP at https://smushbug.com/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 smushbug and sign in.

claude mcp add --transport http smushbug https://smushbug.com/mcp

Using something else? Any MCP client works with https://smushbug.com/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 Smushbug. 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 Smushbug 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 grouped into bugs, ideas and feedback, each with its triage. Arguments: status (open, resolved, ignored or all), kind (bug, idea, feedback or all), triage (new or all), project_id and limit per kind.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
triage_issueRecord a decision with its reason. A bug is confirmed, not_reproducible or fixed; an idea or feedback is planned, declined or shipped. Not reproducible and declined need a reason. Arguments: issue_id, decision 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 three prompts. triage_reports goes through new reports grouped by kind and proposes a decision for each, then waits for your yes. fix_bug takes an optional issue_id (default: the newest open bug), checks the bug is real, fixes it and marks it fixed. build_idea takes the issue_id of an idea you planned, builds it and marks it shipped.

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://smushbug.com/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://smushbug.com/api/v1/issues/ISSUE_ID/status
EndpointWhat it doesNeeds
GET /api/v1/projectsList your projects.Read only
GET /api/v1/issues?status=open&kind=all&triage=new&project=prj_...&limit=20List issues. Filter by status, kind, triage (new or all), 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}/triageBody: { "decision": "declined", "note": "Out of scope for now" }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:

  • 400: the request does not fit, for example a decision that does not match the issue's kind or a decline with no reason.
  • 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.