Install
- Sign in and create a project. You get a project key.
- Paste the snippet before the closing
</body>tag on every page. - 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().
| Attribute | Default | What it does |
|---|---|---|
data-endpoint | required | Where reports are sent. Your project page shows the exact value. |
data-project | default | A label sent with each report. |
data-launcher | bottom-right | Where 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-style | tab | tab 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-error | true | Wiggle the tab once and show a small dot when the page first logs an error. The tab never opens by itself. |
data-label | Report a bug | Name of the launcher: shown on the round button and when the tab is pointed at, and read out by screen readers. |
data-accent | ladybug red | Your brand colour for the send button and annotations, as #rgb or #rrggbb. |
data-theme | auto | light, dark or auto (follows the visitor’s system). |
data-hotkey | ctrl+shift+b | Keyboard shortcut. Set false to turn it off. |
data-title | Report a problem | Heading of the dialog. |
data-placeholder | Placeholder in the description box. | |
data-require-email | false | Ask the reporter for an email address. |
data-mask-inputs | true | Blur every field’s contents in the screenshot. |
data-capture-console | true | Record console errors and warnings. |
data-capture-network | true | Record failed requests. |
data-capture-breadcrumbs | true | Record 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
Add this to ~/.cursor/mcp.json. Cursor asks you to sign in the first time it connects.
{
"mcpServers": {
"smushbug": { "url": "https://smushbug.com/mcp" }
}
}
Add this to .vscode/mcp.json, then start the server and allow the sign in.
{
"servers": {
"smushbug": { "type": "http", "url": "https://smushbug.com/mcp" }
}
}
In Claude, open Settings, then Connectors, then Add custom connector, and sign in when asked.
Name: Smushbug URL: https://smushbug.com/mcp
Turn on developer mode in ChatGPT settings, add a custom MCP server with this URL, and choose OAuth.
Name: Smushbug URL: https://smushbug.com/mcp Authentication: OAuth
Run both lines in your terminal. The second one opens the sign in.
codex mcp add smushbug --url https://smushbug.com/mcp codex mcp login smushbug
Add this to ~/.gemini/settings.json, then type /mcp auth smushbug in Gemini CLI.
{
"mcpServers": {
"smushbug": { "httpUrl": "https://smushbug.com/mcp" }
}
}
Add this to opencode.json. OpenCode signs in on first use, or run opencode mcp auth smushbug.
{
"mcp": {
"smushbug": { "type": "remote", "url": "https://smushbug.com/mcp", "enabled": true }
}
}
Add this to ~/.codeium/windsurf/mcp_config.json and sign in when Windsurf asks.
{
"mcpServers": {
"smushbug": { "serverUrl": "https://smushbug.com/mcp" }
}
}
Add this to ~/.hermes/config.yaml, then run hermes mcp login smushbug.
mcp_servers:
smushbug:
url: "https://smushbug.com/mcp"
auth: oauth
Run both lines. The second one opens the sign in.
openclaw mcp add smushbug --url https://smushbug.com/mcp --transport streamable-http openclaw mcp login smushbug
In Grok, open Settings, then Connectors, then New Connector, choose Custom and sign in when asked.
URL: 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
| Tool | What it does | Needs |
|---|---|---|
list_projects | List the projects in your account. | Read only |
list_issues | List 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_issue | Read 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_status | Set an issue to open, resolved or ignored, with an optional note. Arguments: issue_id, status and note. | Read and write |
triage_issue | Record 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_note | Add 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
| Endpoint | What it does | Needs |
|---|---|---|
GET /api/v1/projects | List your projects. | Read only |
GET /api/v1/issues?status=open&kind=all&triage=new&project=prj_...&limit=20 | List 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}/status | Body: { "status": "resolved", "note": "Fixed in #482" } | Read and write |
POST /api/v1/issues/{id}/triage | Body: { "decision": "declined", "note": "Out of scope for now" } | Read and write |
POST /api/v1/issues/{id}/notes | Body: { "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.