Fruitback Docs
fruitback.com GitHub
All the docs

Docs Reference Worker responses

Worker responses

A plain Node HTTP process: node:http adapted onto a web-standard handler, no framework.

Route Status
POST /feedback plants a note in the configured store, answers 201 with the issue: id, identifier, and url when the store has a page to open
GET /feedback?url=…[&client=…] the notes of that page: anchor, note, state, stage
OPTIONS /feedback CORS preflight, never reaches the store
GET /health 200 when the configuration is valid, 503 naming what is wrong when it is not

The four below are the browser extension’s session, and they are exempt from ALLOWED_ORIGINS: an extension’s origin carries an id that differs between an unpacked build and a store build, so an operator cannot put it on a list. The rate limiter is what protects them, which is why it runs above the path dispatch.

Route Status
POST /session/pair spends a pairing code, opens a session
POST /session/refresh a refresh token for a fresh access token
POST /session/revoke ends the session; 204 whether or not there was one to end
GET /pair the page a pairing link opens: static, no script

A reviewer connects the extension from the console: « Connect this browser », on « Put it on the site » or on « Sites », opens the pairing link of your worker (on Fruitback Cloud the same button leads to the sign-in of the popup instead, and its worker answers 404 on the pairing routes). An operator can also mint a pairing code on the container, never over HTTP, for one workspace and one of its members. The workspace id is in the address of the console, /w/<id>/sites, and the workspace must have a site. --subject is the id of the member’s account, which the database gives from their address:

docker compose exec -T postgres psql -U fruitback -d fruitback -Atc \
  "SELECT id FROM \"user\" WHERE email = 'alice@acme.dev'"
docker compose exec worker node server.mjs pair --subject <account id> --name "Alice Martin" --workspace <id>

With a dedicated schema, add SET search_path TO <schema>; before the SELECT. The command refuses, and prints no code, when the workspace is missing, when it has no site, and when the account is not a member of it: the route would end such a session at once.

Add --endpoint https://api.feedback.example.com, the address reviewers reach this worker at, and the command also prints a link, https://api.feedback.example.com/pair#<code>. The reviewer opens it and clicks the extension: nothing to copy. The code is after the #, so it reaches no server and no access log, yours included. An http:// address is refused unless it is localhost. Measured: the command writes a code valid 15 minutes. Not measured for this page: a session of that code reading a site.

The two paragraphs below describe the linear store. A worker on another store does the same job by its own means: see apps/worker/src/store.ts, where findForPage states the intention and never the method.

Labels are created on demand, so a new client site needs no manual Linear setup. A label that cannot be created is dropped and the feedback still goes through: losing a label is a triage annoyance, losing the client’s note is a bug.

The read path is one Linear query, narrowed server-side by the fruitback label, the per-client label and description contains <canonical url>. contains being a substring match, the note’s own page.url is re-checked exactly, or /pricing would return the pins of /pricing?tab=annual. Answers are cached for 15 s: the same page opened by a room full of reviewers costs one call against the Linear quota, and a failed call is never cached.