bugpot

Documentation

Everything you need to ship the widget

One script tag to install, a small config object to shape it, a JavaScript API when you want full control, and the full reference for every other Bugpot surface: website checks, the inbox, sign-in, emails and the REST API. No rabbit holes.

Quick start

Create a project in the dashboard, copy its key, and paste this before your closing </body>tag. The loader is async, renders inside a Shadow DOM so it can never clash with your CSS, and stays under 150 KB gzipped.

index.html
<script
async
src="https://app.bugpot.io/embed/loader.js?p=your_project_key">
</script>

Reload your site, the Feedback button appears bottom-right, send yourself a test report.

Configuration

The widget ships configured from your project settings in the dashboard: position, label, branding, forms and issue types all live there. To override per page, define window.BugpotConfig before the loader tag.

index.html
<script>
window.BugpotConfig = {
buttonPosition: 'bottom-left',
buttonLabel: 'Report a bug',
brandColor: '#28d48a',
language: 'auto',
metadata: { build: '2026.07.1', tenant: 'acme' },
};
</script>
<script async src="https://app.bugpot.io/embed/loader.js?p=your_project_key"></script>
OptionTypeDefaultWhat it does
buttonPosition'bottom-right' | 'bottom-left' | 'bottom-center' | 'hidden''bottom-right'Where the launcher sits. Use hidden to drive the widget from your own button via the JavaScript API.
buttonLabelstring'Feedback'Text on the launcher button.
buttonIconOnlybooleanfalseShow just the pin icon, no label. Handy on small screens.
brandColorstring | nullnullHex colour like "#28d48a" for the launcher and accents. Defaults to your workspace branding.
maskingSelectorsstring[][]CSS selectors blurred in the browser before the screenshot is captured.
showFooterbooleantrueHide the "Powered by" footer in the widget (available on the Agency plan).
metadataRecord<string, unknown>{}Key-value data attached to every report: build number, tenant, feature flags.
language'en' | 'de' | 'fr' | 'es' | 'it' | 'nl' | 'pt-BR' | 'pl' | 'auto''en'Widget UI language. 'auto' picks from navigator.languages. Falls through to the project's configured language when omitted.

Two more settings live on the project rather than in this bag, so a marketer can change them without touching HTML: audience (Everyone, Team members only, or Guests only) and page rules (glob include and exclude lists for the URLs the widget shows on). The loader bakes both into the bundle it serves you, and the widget re-checks them on every navigation so single-page apps work without a reload. Details are in the widget targeting section below.

JavaScript API

Once loaded, the widget exposes window.Bugpot. Hide the built-in launcher and open the widget from any element of your own:

custom-launcher.html
<script>
window.BugpotConfig = { buttonPosition: 'hidden' };
</script>
<script async src="https://app.bugpot.io/embed/loader.js?p=your_project_key"></script>
<button onclick="window.Bugpot?.open()">Give feedback</button>
  • Bugpot.open()Open the widget; screenshot capture starts immediately.
  • Bugpot.close()Close the widget and restore the launcher (unless hidden).
  • Bugpot.destroy()Remove the widget from the page entirely.
  • Bugpot.versionThe running widget version string.
  • Bugpot.metadataMutable metadata bag, read at submission time (see below).
  • Bugpot.signIn()Prompt the visitor to sign in as a team member. Opens a short popup to bugpot.io; resolves once the popup returns. Sessions last twelve hours in that browser.
  • Bugpot.signOut()Forget the current team-member session for this browser. The widget goes back to the guest form on the next open.
  • Bugpot.memberThe signed-in team member, or null. Read this from your own header to decide whether to show a bespoke trigger.

The loader waits for window.BugpotConfig, so anything you set on the config bag is read once at boot. Anything on window.Bugpot is read every time it is used, so it is safe to mutate Bugpot.metadata as your app state changes.

Custom metadata

Anything you put in the metadata bag rides along with every report and shows up in the triage inbox. Seed it in the config, or mutate it at runtime as your app state changes. It’s read at the moment a report is submitted.

app.js
// Seeded before load...
window.BugpotConfig = {
metadata: { build: '2026.07.1', plan: 'studio' },
};
// ...and updated whenever you like after load
window.Bugpot.metadata.userId = 'u_4821';
window.Bugpot.metadata.cartId = 'c_812';

Values from custom form fields (severity pickers, dropdowns, checkboxes you add in the form builder) arrive as custom metadata on the report too.

Data masking

Anything matching your masking selectors is blurred in the browser, before the screenshot is created. Account numbers and customer data never reach Bugpot’s servers in the first place.

index.html
window.BugpotConfig = {
maskingSelectors: ['.account-number', '[data-private]', '#billing-email'],
};

More on the pipeline in Product, Data masking.

What gets captured

Alongside the annotated screenshot, every report carries the context your developer would otherwise have to ask for:

  • Page URL and title
  • Browser and OS (user agent)
  • Screen and viewport size
  • Device pixel ratio
  • Console output (log to error)
  • Failed network requests
  • Reporter name and email
  • Your custom metadata

Console and network capture are toggled per project in the dashboard.

Website checks

Website checks give every Bugpot project an on-demand quality report. Press Run scan and Bugpot opens each page the way a browser would, notes what it sees, and turns anything worth your attention into a finding you can act on or set aside. A scan respects your site’s robots.txt and waits half a second between page loads, so it will never look like a bot attack from your own analytics.

Content

The writing and structure a search engine cares about. Missing or duplicate titles, meta descriptions that are too short or too long, missing or repeated H1s, headings that skip a level, pages thin on words, reading level, and links that go nowhere.

Accessibility

axe-core against the WCAG 2.2 AA rule set. Colour contrast, missing alt text, form labels, focus order and the other things a screen reader stumbles over.

Compliance

What a regulator would ask. Is the page secure, does it load anything over http on an https page, is there a cookie banner where there is third-party tracking, is there a link to a privacy policy and to terms of service, and does every form that collects personal details give somebody something to consent to.

Every finding carries a severity from critical down to info, and the health score you see on the ring is those severities added up and subtracted from 100. Each category gets its own score too, so a red bar on accessibility does not hide a green one on content. A finding you have dismissed or snoozed does not count against your score, which means acting on the advice actually moves the number. Snoozing hides a finding for a set number of days if you want to look at it again later; restore brings it back. Everything is keyed to the element it points at, so a fix to that element makes the finding go away, and a dismissal survives the next scan.

PlanPages per scanSchedule
Solo25 pages per scanOn demand or weekly
Studio50 pages per scanOn demand, daily or weekly
Agency100 pages per scanOn demand, daily or weekly

When a scan finishes, the “since last scan” strip at the top of the report tells you what changed: new findings, fixed findings, and how many are still there. That is the number to watch over time.

Inbox: saved views, duplicates and links

Saved views

Save the views you use every day. Filter the inbox the way you want it, then save that combination as a view. The next time you open the inbox, one click puts you back where you were. Views live in the toolbar next to Filters.

A view starts private, so it only shows up for you. Tick “Share with the workspace” to hand it to the rest of the team. Workspace admins can rename or remove any shared view, so nothing lingers when someone leaves.

Duplicates and related links

Every issue drawer now has a Linked issues card. “Mark as duplicate of...” closes this report and points it at the canonical one, so your open workload reflects reality. Removing the link puts the closed report back into the state it was in before. “Link related issue...” ties two reports together without closing either one, so a fix on one can point somebody at the other.

If you mark a report as a duplicate of one that was already a duplicate, the link jumps to the canonical for you. Duplicates carry a small badge in the inbox, and a “Hide duplicates” toggle in the toolbar collapses them into their canonical when you want a cleaner list.

Team member sign-in and widget targeting

Team member sign-in

Anyone on your team can attach their identity to bug reports without typing their name and email each time. When they open the widget they see a small “Team member? Sign in” link next to the type picker. Signing in opens a short popup to bugpot.io. If they are already logged in the popup closes on its own; if not, they log in there once and come back. Reports they file from that browser land in the inbox with a “Team member” badge and their real name, and the widget switches to the Member form, which usually hides the name and email fields since we already know who they are.

Sign-in lasts twelve hours in that browser, then the widget quietly forgets it. Removing someone from your workspace stops their next submission immediately: the widget shows them a sign-in error rather than accepting the report under their old identity. If you build your own trigger with the JavaScript API, call window.Bugpot.signIn() from your app’s own header, or read window.Bugpot.member to know whether somebody is signed in before showing the widget.

Widget targeting

Two settings on Project settings, Widget, Button control who sees the launcher and where it appears. Audience switches between Everyone (the default), Team members only, and Guests only. In Team members only mode the floating button becomes a Sign in prompt and only opens the report form after somebody signs in, which is useful for staging or internal apps. Guests only hides the sign-in link entirely.

Page rules restrict which URLs the widget renders on. Add glob patterns like /blog/*, /checkout, or a bare * for every page to the Include list, and any patterns you want to leave out to the Exclude list. * matches one path segment, ** matches any run of characters including slashes, and Exclude always wins. The widget re-checks these rules whenever the URL changes, so single-page apps work without a reload.

Both audience and page rules are baked into the loader from your project settings, so they update when you save them in the dashboard, no code change on your site.

Signing in: magic link, two-factor, passkeys and domain auto-join

Magic link

Forgot your password, or never chose one? On the sign-in page, tap “Email me a sign-in link” instead, type the address you use with Bugpot, and we send you a link that signs you in with one click. The link works once and lasts fifteen minutes. If somebody else asked for a link on your address by mistake, ignore the email and nothing changes on your account.

Receiving the link is proof that the address is really yours, so a magic-link sign-in also confirms your email if it was still unconfirmed. Your session, sign-out and device notifications behave exactly the same way as if you had typed your password. If we cannot send email from your server yet, the option is hidden on the sign-in page rather than shown as a button that would not work.

Two-factor authentication

Add a second step to every sign-in by pairing an authenticator app (1Password, Authy, Google Authenticator, any TOTP app) from Settings, Profile, Security. From then on the sign-in page asks for the six-digit code from your app after your password.

When you switch it on we show you ten one-time recovery codes. Keep them somewhere safe: they are the way back in if you lose the device with your authenticator on it, and we only show them once. To turn two-factor off again you need your password and a current code, so a stolen session cookie cannot disable it on its own.

Passkeys

Sign in with Touch ID, Windows Hello, a Yubikey, or your phone. Add one from Settings, Profile, Security and pick it on the sign-in page instead of your password. Passkeys are per-device, so pair every browser you use.

Domain auto-join

In Settings, Workspace, Authentication, set the domain your team owns, for example bugpot.io, and everyone who signs up with an email at that domain and confirms it joins your workspace automatically. Pick whether new joiners land as a Member or a Guest. You can only claim a domain you can receive mail at yourself, so verify your own email first and make sure your address is at that domain, then Save.

Public mailbox domains like gmail.com or outlook.com cannot be claimed, because they are shared by millions of unrelated people. A domain can only belong to one workspace at a time, so if another Bugpot workspace has already claimed yours, contact us and we will sort it out. Existing memberships are never overwritten, so a colleague who is already a guest is not silently promoted, and turning the feature off later does not remove people who have already joined.

Emails Bugpot sends

Bugpot writes to you in two kinds of moments. The first is the transactional side, which is anything about your account or your workspace: welcomes when you sign up, warnings when a trial is nearly up, a heads-up when a project is running short on screenshot storage, and the alerts about failed payments. These arrive whether you have notifications turned on or off, because they are about the account rather than the workflow. The one thing that will silence them is a bounced address, and the reason we go quiet then is to stop the sending domain being blocked, not because we have stopped trying to reach you.

The second kind is optional: install nudges when a project has still not loaded the widget a day after you added it, and a Monday-morning weekly summary of everything that happened in your workspace over the previous seven days. Both of these carry a one-click unsubscribe link in the footer, and your email client’s own unsubscribe button will work on them too. You can also switch these off from Settings, Profile, Notifications, which will silence every non-transactional email Bugpot ever sends you, weekly summaries and issue notifications alike.

REST API

The public REST API is part of the Agency plan. Solo and Studio workspaces cannot issue keys, and a key whose workspace has since dropped below Agency stops working immediately. Upgrade from Workspace settings, Billing & plans before creating a key.

Authentication

Create a key from Workspace settings, API keys, then send it in the Authorization header as Bearer bp_live_.... Keys are shown once at creation and never again, so save yours to your secrets manager or environment file straight away. Every key is scoped to one workspace and holds an explicit set of permissions: issues:read, issues:write, comments:write and projects:read.

Requests missing a key, or holding an unknown, revoked or expired one, come back as 401 unauthorized. A key that authenticates but does not hold the scope the endpoint asks for gets a 403 forbidden. If the workspace’s plan no longer includes API access, the response is 401 with a message saying so.

Base URL

All endpoints live under https://bugpot.io/api/v1. Content type is JSON in and out; the only exception is GET /issues/:id/screenshot, which streams the PNG. The API does not read cookies and does not open CORS: it is designed for backend integrations, not for a browser.

Endpoints

List the projects in your workspace (scope: projects:read):

list-projects.sh
curl https://bugpot.io/api/v1/projects \
-H "Authorization: Bearer bp_live_..."

List issues (scope: issues:read). Optional filters: status, type, priority, assigneeId, and since (an ISO 8601 timestamp; issues submitted at or after this point):

list-issues.sh
curl "https://bugpot.io/api/v1/issues?status=new&since=2026-09-01T00:00:00Z" \
-H "Authorization: Bearer bp_live_..."

List issues in one project:

list-project-issues.sh
curl https://bugpot.io/api/v1/projects/prj_abc/issues \
-H "Authorization: Bearer bp_live_..."

Fetch a single issue with its comments and screenshot metadata (scope: issues:read):

get-issue.sh
curl https://bugpot.io/api/v1/issues/fb_xyz \
-H "Authorization: Bearer bp_live_..."

Download the screenshot for one issue (scope: issues:read). The response is image/png:

get-screenshot.sh
curl https://bugpot.io/api/v1/issues/fb_xyz/screenshot \
-H "Authorization: Bearer bp_live_..." \
--output screenshot.png

Update status, priority or assignee, any subset of the three (scope: issues:write). An assignee id must belong to an active member of the workspace, else the response is 400 validation_failed:

update-issue.sh
curl -X PATCH https://bugpot.io/api/v1/issues/fb_xyz \
-H "Authorization: Bearer bp_live_..." \
-H "Content-Type: application/json" \
-d '{"status":"resolved","priority":"high"}'

Post a comment as the integration itself (scope: comments:write). No person is credited on the timeline; the entry is attributed to “API key”:

post-comment.sh
curl -X POST https://bugpot.io/api/v1/issues/fb_xyz/comments \
-H "Authorization: Bearer bp_live_..." \
-H "Content-Type: application/json" \
-d '{"body":"Deployed a fix in v1.4.2."}'

Pagination

Every list endpoint pages by cursor. Ask for a page with ?limit= (up to 100, default 50). If the response body carries a non-null nextCursor, pass it back as ?cursor= on the next request. When nextCursor is null, there are no more pages.

paginate.sh
curl "https://bugpot.io/api/v1/issues?limit=50&cursor=fb_xyz" \
-H "Authorization: Bearer bp_live_..."

Rate limits

Each API key is limited to 600 requests per minute. The response carries the standard RateLimit, RateLimit-Policy, RateLimit-Remaining and RateLimit-Reset headers so a well-behaved client can pace itself. Bursting past the limit gets a 429 rate_limited. Wait until the reset time and retry.

Error shape

Every error comes back as JSON with a stable error code and a human-readable message:

error.json
{ "error": "forbidden", "message": "This API key does not hold the issues:write scope" }
CodeHTTPMeaning
unauthorized401The key is missing, unknown, revoked, expired, or the workspace plan no longer includes API access.
forbidden403The key authenticated but does not hold the scope the endpoint needs.
not_found404The resource does not exist, or belongs to another workspace. Same code either way, so a key cannot be used to probe which ids exist in another tenant.
validation_failed400The request body or query is malformed. The details array names the offending path.
plan_limit_reached402The workspace hit a plan limit. details carries { currentPlan, upgradeTo }.
rate_limited429Too many requests. See rate limits above.

Attachments and widget languages

Attachments

Files can be attached to any report, both from the widget on your site and from the dashboard when you open an issue. Reporters can add up to three files at a time, up to 5 MB each: images (PNG, JPEG, GIF, WebP), PDFs, and text or log files. On the widget the picker sits on the review step next to the screenshot and the description. In the dashboard, each issue drawer has an Attachments section with previews for images, download for everything else, and a delete control for anyone who can edit the issue.

If a file fails to upload, the report itself still goes through and a short note says what happened. Attachments count towards the website’s storage quota, and they never expire, even on plans where old screenshots are cleaned up after a while.

Widget languages

The widget speaks eight languages: English, German, French, Spanish, Italian, Dutch, Brazilian Portuguese and Polish. Every visible string is translated, including the type picker, the annotator tooltips, error messages and the confirmation screen. Choose the language on the Widget, Button page under Language. Pick a fixed language if your site is single-language, or pick Auto and the widget matches the visitor’s browser language, falling back to English for anything it does not have.

A developer can override the language for one page by setting window.BugpotConfig.language before the loader runs. The embed script itself is now served with a content hash and long cache headers, so a visitor always gets the current build without your page having to change.

Personal connected accounts and form destinations

Personal connected accounts

Sign in to the trackers your team uses under your own name. When Bugpot pushes a report to a connected tracker, it can either use the workspace’s shared connection or your personal sign-in, so the ticket appears under your name in the destination tool. Auto-push and status sync always use the workspace connection, so nothing about the shared setup changes.

Go to Settings, Profile, Connected accounts, pick a tracker and choose Connect. From then on, any push you trigger from Bugpot lands in that tracker as if you had opened the ticket by hand. Sign out at any time and future pushes fall back to the workspace connection. If your personal sign-in expires without you noticing, Bugpot uses the workspace connection for that one push and tells you at the top of the accounts page so you can reconnect.

Form destinations and field mapping

Route reports through the tracker that matches the form they came from. On both the Guest and Member forms builder there is a Destination card at the top of each form: choose any connected tracker for reports from this form to land in, or leave it on “Follow the auto-push rules” to keep the project-wide behaviour.

Below that, each field can be mapped to a target in the destination ticket. Map a short answer to the ticket title to use it as the headline, a dropdown to Priority to feed the priority column, anything to Labels to add tags, and a long answer to the description to append it under its own heading. If the tracker supports custom fields, extra targets appear in the list; if it does not, the builder says so and the mapping is ignored rather than failing silently at push time.

Guests scoped to a website

When you invite a guest to your workspace, you can pick which websites they see. Choose “Guest” as the role on the invite form and a project list appears. Tick the ones this person should have access to, or leave everything unticked to give them every project, which is the default. The choice is stored on the invite and copied onto the person’s membership the moment they accept.

Once a guest has joined, admins can change what they see at any time: open the member from the members list and tick or untick projects under “Guest scope”. A guest with a scope only sees the projects on their list, everywhere in Bugpot: the project list, the feedback inbox, search and their notifications. A project outside their scope looks to them as if it does not exist, so they cannot go looking for something they were not meant to find. Members and admins are unaffected; the scope only ever applies to guests.

Integrations & webhooks

From 1 October 2026 you’ll connect Jira, GitHub, GitLab, Linear and eleven more trackers from workspace settings; each integration page has its own step-by-step setup. Prefer to build your own pipeline? Webhooks are live today: they deliver every event as JSON, signed with HMAC so you can verify the sender, with a replayable delivery log for when your endpoint was down. See Security for the details.

Support

Stuck on anything? Write to hello@bugpot.io. A human answers. Live service health is on the status page.

Put a feedback button on your site today

Paste the script tag, send yourself a test report, and see the full context arrive in your inbox.