.fyi
Early access

How it works

You want to hear what's wrong with your app in the tracker you already use, ready to fix, without another company holding your users' notes. quibble does it with three pieces, all yours: a script tag in your pages, a Python package inside your app, and your tracker. Nothing else to quibble about in between.

  1. Add it to your app

    Install the package, give it a token, mount the endpoint and add the button. About ten minutes.

  2. People point and say it

    The widget adds the page, the element they clicked and the page's last errors.

  3. Your app files the issue

    With a token only your app holds, straight into your tracker.

Set it up Or hand it to your coding agent

Where a note goes

A user clicks the button and writes a sentence. The widget sends it, with the page context, to an endpoint mounted in your own app. Your app files it as an issue using one service token that only your app holds.

That's the whole path. quibble has no telemetry and makes no call home, and the widget's script is served by your app from your own domain, so it adds nothing to your content security policy.

quibble
  1. Their browserA user spots a problem and clicks quibble
  2. Your appquibble adds the page, the error and who sent it
  3. Your to-do list GitHub, Jira, Linear…It arrives as an issue, ready to fix

Nothing passes through quibble. There’s no quibble server to pass through.

Add it to your app in four steps

About ten minutes, and most of that is making the token. Whatever your framework and tracker, the steps are the same:

  1. Install the package. Add quibble to your app's dependencies. Early access comes with the install line. It's free for individuals and small companies; larger ones, email us.
  2. Give it a token. In GitHub, GitLab, Gitea, Forgejo, Jira or Linear, make a token that can create issues where notes should go, and add it to your app's settings.
  3. Mount the endpoint. One line where your app starts, behind the sign-in your app already has.
  4. Add the button. One line in your base template, and every page has it.

Step by step, with the code for your framework and tracker

Or hand it to your coding agent

The three pieces

The widget

One script tag. Vanilla JavaScript with no dependencies and no build step. It draws the button and panel inside a shadow root, so your styles can't reach it and it adds none to your page. Single-page apps can mount it and unmount it as routes change.

The adapter

A Python package with no dependencies of its own. Mount it on FastAPI, Django, Flask, Starlette or Litestar in a couple of lines. Others can integrate using a documented schema.

Your tracker

Each note becomes an issue carrying a marker label, so feedback is easy to filter from the rest of your work. There's no database: the tracker is the datastore.

One note, step by step

  1. The widget loads from your app. One script tag. It renders inside a shadow root, reads its settings from your page, and waits.
  2. On open, it asks your app what's allowed. GET /quibble/meta?url=… returns the labels and counts of the repository this page's feedback goes to. Your app decides the target, so the tags offered are the tags that will be accepted.
  3. The person writes a sentence. The widget gathers the page URL, title, referrer, viewport and browser, the element they pointed at, the last errors, and any fields your page declares in window.QUIBBLE_CONTEXT. Masked regions report (masked).
  4. It posts one JSON request to your app. POST /quibble with the text, priority, tags and context. Nothing goes anywhere else.
  5. Your app's adapter does the work. It applies the rate limit, picks the target repository, holds the priority to P1–P3, drops tags that don't exist, renders the issue body with diagnostics folded away, and creates the issue with your service token.
  6. The widget confirms. The response names the issue number, and the person sees it filed.
One note from a billing page, then the issue quibble wrote, top to bottom: the page, who sent it, what they pointed at, your app’s context, and the error the customer never saw. Recorded on the live demo.

The endpoints

Five routes under one mount path you choose (default /quibble). The widget only uses the first two; the rest serve the review page.

MethodPathDoes
POST/quibbleFiles a note as an issue
GET/quibble/metaLabels and counts for the target repository
GET/quibbleLists quibble-filed issues, newest first
GET/quibble/reviewThe review page
POST/quibble/{number}/statusCloses or reopens an issue

Plain JSON over HTTP. The FastAPI, Django, Flask, Starlette and Litestar adapters implement it; anything else can host quibble by implementing the same documented schema.

On your computer, any language

A companion on your computer. quibble serve runs the widget and its endpoint beside an app written in Node, Rust, Go or anything else: a desktop app, a local tool, or the app you're developing. It listens only on that machine, accepts only the origins you list, and can require a shared key, so nothing else on the network can file through it.

A hosted app in another language implements quibble's documented backend schema: the same five endpoints, so the widget doesn't change and the path still ends at your tracker. quibble serve stays local by design; there is no networked service to run beside a server.

The steps, in the install recipe · all three places quibble works

With quibble serve, on one computer
  1. Your computerYou spot a problem in your appquibble, running beside it, adds the details
  2. Your to-do list GitHub, Jira, Linear…It arrives as an issue

Only the issue leaves your computer, sent straight to your to-do list.

What arrives with every note

Always

What they wrote, the page they were on, its title, where they came from, their screen size, browser, language and time zone.

Who sent it

Their name from the sign-in you already have. Without one, they can type it, and the issue says it's self-declared.

When something broke

The last five JavaScript errors the page threw, folded away under Diagnostics until you want them.

When they choose

The element they pointed at. A screenshot, with any region you mark private blanked out.

Additional context you need

Console output, failed network requests and any fields your app wants to add. Off until you turn them on.

What quibble writes to your repository

Worth knowing before you point it at a repository that matters. This is the complete list:

  • Issues, each carrying the marker label.
  • Labels: the marker, and P1, P2, P3, created only if missing. Never anything else: a tag your tracker doesn't have is dropped, not created.
  • On GitHub only, one branch, quibble-screenshots, and only if screenshots are used; other trackers, Jira and Linear included, attach the image to the issue. You can also keep screenshots on your own server, so the tracker gets only a link. It never touches your default branch.

No comments, no closing, and no edits to anything quibble didn't create.

Everything else it does

Point at it

“This button, here.” People click the element that's wrong, and its selector and text go into the issue.

Private stays private

Mark a region private and its text is never captured and it's blanked out of screenshots.

Your styles can't break it

The widget lives in a shadow root. Your CSS can't reach it, and it adds none to your page.

One button, many repositories

Route notes by page or let people pick. Discover every repository in an org, and mix GitHub with a self-hosted Gitea.

A review page

Every quibble in one list, filtered by open or closed, with close and reopen one click away.

Tags you choose

Offer all your labels, an allowlist, or everything except a blocklist.

Rate-limited writes

Each signed-in sender gets a budget, so one enthusiastic user can't flood your tracker.

An off switch

Set one variable and your app mounts no quibble routes at all.

quibble doctor

Checks a deployment's token, repositories and labels, and reports what's wrong without printing a secret.

quibble demo

Runs a sample app with every feature reachable, offline, with no token.

A remark when you file

If the team switches it on, the panel answers your note with a one-line remark, like “yeah, we know. tell us anyway.” or “let me guess: it worked yesterday.” It's off unless they turn it on, and they pick the tone.

A test kit

In-memory fakes of each supported tracker ship with quibble, so your own tests run with no network.

Which trackers and languages work

Trackers

TrackerStatus
GitHubIn production use
GiteaTested on 1.27.3
ForgejoTested on 12.0.4
GitLabTested on 19.4.1 CE
Jira CloudTested on Jira Cloud, 2026-09-30
LinearTested on Linear, 2026-10-08
GitHub Enterprise ServerNot yet tested
Jira Data Center / ServerRoadmap
Azure DevOpsUnder consideration
RedmineUnder consideration
Bitbucket Data CenterUnder consideration

Languages

LanguageStatus
PythonSupported
Any other stack
By implementing quibble's documented schema.
Supported
Apps in any language on your computer, with quibble serve as a companion. A hosted app in another language implements quibble’s documented schema.
Local apps

Every tested version is one we ran quibble against, not one we read about. And we report quibble's own problems with quibble: in each framework it supports, beside a non-Python app, in a single-page app, in the extension and on this site. GitHub Enterprise Server ships as a licensed appliance we can't stand up, so it stays marked untested until someone runs it. Supported rows are generated from quibble 2.0.0 itself; roadmap rows are plans, not promises. Screenshots work on every supported tracker.

Ready to add it?

Four steps, with the code for your framework and the settings for your tracker. Or see it first: the live demo runs quibble itself in your browser, with nothing to install.

Set it up Hand it to your coding agent Try it live