Chrome extension
The X3D browser extension: capture a region of any page, turn it into a 3D model, order the print — how pairing works, what its token can do, and what it cannot.
The X3D browser extension captures a region of whatever page you are on, sends the image to the model generator, and lets you order the result printed and posted without leaving the tab. It is a Manifest V3 Chrome extension that talks to the same generation and print endpoints the website uses, authenticated with its own token.
What it does#
- Capture a rectangular region of the current tab, by click or with Alt+Shift+X.
- Read the page for context — schema.org product data, OpenGraph tags, or the h1 — to pre-fill the prompt.
- Warn when a capture comes back blank, which is what a DRM-protected video looks like to a screenshot.
- Send the image to the generator and poll for progress. Generation runs in the service worker, so you can close the popup and it keeps going.
- Order the finished model printed and shipped, with a shipping form in the popup.
- Show your credit balance and whether a card is on file, at the moment you open the popup rather than at the moment you submit.
Chrome 116 or newer. Permissions requested are activeTab, scripting and storage, with one host permission: https://x3dstudios.com. It has no permission to read any other site in the background.
How pairing works#
Pairing follows the device-authorization pattern used by TV sign-in flows. The extension invents a secret, you approve that secret while signed in on the website, and the extension exchanges it for a token. The alternative — the website messaging an extension directly by ID — was rejected deliberately: the extension ID would arrive in the URL, so anyone who could get you to open a link could have a token minted into an extension of their choosing.
- 1The extension generates a nonce
32 random bytes as 64 hex characters. Both pairing routes reject anything that is not exactly that shape with 400 and the message Malformed pairing code.
- 2It opens the consent page
/connect/extension?code=<nonce>&label=<name>. The page lists what is being granted in plain terms: spending generation credits, placing print orders that charge your card, and reading your credit balance. If you are signed out it sends you to sign in and comes back.
- 3You approve
The page posts to /api/extension/pair/approve, which is session-authenticated only. A bearer token cannot reach it — otherwise an extension that already had access could mint itself more. Only the hash of the nonce is stored, so the row is worth nothing if it leaks.
- 4The extension claims the token
It polls /api/extension/pair/claim with the nonce it kept. Before approval that returns 202 with { pending: true } — polling while the consent tab is open is the normal case, not an error. After approval it returns the token once, and the pairing row is deleted in the same transaction, so a replayed claim finds nothing.
- 5The token is stored in the extension
It looks like x3d_ext_ followed by 48 hex characters. The server keeps only a SHA-256 hash plus a short non-secret display prefix.
The pairing and session endpoints#
/api/extension/pair/approveSigned inThe consent page's submit. Body: { code, label }. Signed-in browser session only.
/api/extension/pair/claimNo authExchange the nonce for a token. Unauthenticated by design — the nonce is the credential. 202 while pending.
/api/extension/meExtension tokenBearer x3d_ext_… — returns email, name, credits and hasPaymentMethod. 401 { error: not_connected } for a revoked token.
/api/extension/revokeSigned inDisconnect. Body: { id } or { all: true }. Session only — a token must not be able to manage tokens.
codestringrequired- The pairing nonce: exactly 64 hex characters. Anything else is 400 Malformed pairing code.
labelstringoptionaldefaultChrome extension- A name for the connection, truncated to 60 characters. Shown on the consent page so you know what you are approving.
What the token can do#
| Endpoint | Accepts an x3d_ext_ token? | Notes |
|---|---|---|
| POST /api/generate | Yes | Checked before the session cookie. Rate limit, credits and history all key off your email, so it is billed exactly like a request from the website. |
| POST /api/generate/start | Yes | Async generation. Returns { jobId } straight away and runs in the background. |
| GET /api/generate/status?jobId=… | Yes | Poll for progress. 403 if the job belongs to someone else. |
| POST /api/print/orders | Yes | Place a print order, including by modelUrl for a model X3D already stores. |
| GET /api/print/orders/{code} | No | Status reads accept only an x3d_live_ API key. See the limitation below. |
Ordering by modelUrl is why the extension does not have to download and re-upload a model it just generated. A generated model already sits in X3D storage behind a gs:// URI the browser cannot fetch; the order route accepts that URI directly and re-checks that the model belongs to your account before using it.
Limits worth knowing#
- Generation spends credits, at the same rate as the website. Every extension generation starts from an image, which adds one credit to the base: a fast draft from a capture costs 2 credits and a hi-fi one costs 3, before any mesh-quality or texture-resolution options.
- Generation is rate limited at 20 starts per hour per account, shared with the website.
- Placing an order charges the card on your account. The consent page says so, and every order still needs two presses in the popup — Print & ship it, then Place order. An estimate is shown with the finished model; the amount actually charged is worked out from the file when the order is submitted, which is what the order screen tells you.
- There is no extension manager in /profile yet. The consent page points there, but the page has no extension section — revoking today means calling POST /api/extension/revoke from a signed-in browser.
- Revoking marks the token revoked rather than deleting it, so when a connection was disconnected stays answerable.
- The extension token is separate from your print-API key on purpose. An extension runs inside every page you visit, which is a far more likely place for a credential to leak, so it is minted per connection and revocable on its own without breaking a server integration.
What the generator does with a captured image, and what makes a good capture.
What a generation costs, how refunds work, and where the balance comes from.
The other credential: the x3d_live_ print-API key and how it differs.
How to reach a person, and what to include so the first reply is useful.