Feedback that arrives with coordinates.
One script tag. Every pin arrives with a screenshot, the URL, and an (x, y), in one inbox grouped by page. Work them yourself, or hand the inbox to an agent that closes each one with the PR URL.
<script src="https://pinpoint-router-872952356c0f.herokuapp.com/embed/SITE_KEY.js" async></script>
Paste it before </body>. Nothing else to install.
Three steps from click to fix
Install the snippet
Paste one script tag into your demo site or landing page. That's the whole integration, simple enough to hand an AI agent mid-build.
Collect pins
Reviewers click the exact spot and leave a note. Pinpoint captures the position, the viewport, and a screenshot, then posts it to your inbox, grouped by page.
Close them out
Review pins once, in one place. Mark each one done from the dashboard, or let an agent make the change and close the pin with the PR URL attached. Delete the noise, so the inbox only holds what you've vetted.
Your agent gets the whole scene, not a sentence
"Fix the button on the pricing page" is a guessing game. The agent has to find the page, guess which button, and guess what "fix" meant. So you stop delegating and do it yourself.
A pin isn't a sentence, it's a coordinate. It already knows the URL, the exact spot that was clicked, the screen it was clicked on, and what the page looked like at that moment. That context is collected by the widget and travels with the pin, so the agent reads the whole scene in one authenticated request.
/api/v1/pins?status=pending
{
"project": "yourdemo.site",
"count": 3,
"pins": [
{
"id": 47,
"client_pin_id": "pin-mfk3x9qz-8c1f2d",
"kind": "pin",
"status": "pending",
"body": "Misaligned button on mobile",
"author": { "name": "Jane Doe", "email": "jane@example.com" },
"page_url": "https://yourdemo.site/landing",
"page_title": "Landing",
"x": 304,
"y": 1188,
"x_percent": 0.77949,
"y_percent": 0.41538,
"viewport": { "width": 390, "height": 844 },
"document": { "width": 390, "height": 2860 },
"user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) … Safari/604.1",
"dropped_at": "2026-09-07T18:02:11Z",
"created_at": "2026-09-07T18:02:12Z",
"completed_at": null,
"external_url": null,
"thumbnail_url": "https://pinpoint-router-872952356c0f.herokuapp.com/rails/active_storage/blobs/redirect/eyJfcmFpbHMi…/pin-mfk3x9qz-8c1f2d.png"
},
…
]
}
Any harness, no SDK
It's a REST endpoint and a bearer token. Claude Code, Codex, Cursor, Hermes, or a shell script you wrote on a Tuesday — if it can make an HTTP request, it can work your inbox. Nothing to install on either side.
The prompt is written for you
Every project prints a self-contained brief: the endpoints, the token, what each field means, and the rule about not closing a pin before the change is in. Copy it, paste it into whatever you're running, go do something else.
The agent closes the loop
When the change ships, the agent patches the pin to completed with
the commit or PR URL. The inbox empties itself, and every closed pin points at
the thing that closed it. Nobody re-reads a backlog to find out what's done.
curl -s -H "Authorization: Bearer $PINPOINT_API_TOKEN" \
"https://pinpoint-router-872952356c0f.herokuapp.com/api/v1/pins?status=pending"
Filter by page, by status, or by everything since a timestamp. The same token patches a pin closed. That's the entire integration surface, and the docs cover every field and every limit.
One tag per project
Create a project in the dashboard, copy its snippet, paste it before
</body>. From then on, every pin finds its way to your inbox,
with the page and the coordinates attached.
One tag, zero build
The embed endpoint serves a loader that injects the stylesheet, the widget, and the wiring. Everything loads from Pinpoint's own origin with a subresource integrity hash on the screenshot library. No CDN, no analytics, no other script or request from your page.
Write-only from the page
The only API request the page makes is the POST that files a pin. No pin is readable with the project key; reading takes the API token. A visitor sees only the pins in their own browser storage.
Safe retries
Pins carry a stable client ID, so the loader can re-post anything unsynced on the next page view and Pinpoint answers with the same row instead of a duplicate. The loader itself is served no-store with versioned asset URLs, so a page never pins itself to a stale widget.
Screenshots that travel
The 500×250 thumbnail is stored and hosted by Pinpoint, so the inbox and the agent API both show what the reviewer saw.
Keys are public, abuse isn't
Project keys live in HTML by design. Intake is capped at 60 posts a minute per IP and 3 MB per request; a screenshot over 2 MB is dropped and the pin still lands. A key can only file feedback into its own project, so the worst a leaked key can do is file feedback.
An open widget underneath
pinpoint.js is MIT and
framework-free. What your page loads is under 15 KB of JavaScript and 5 KB of
CSS, gzipped. html2canvas (63 KB gzipped) loads only when someone enters pin
mode. What you're looking at is its documented onPinAdd hook,
fully realized.
Let the feedback route itself
One script tag on your site. Every pin lands in one inbox with the screenshot, the page, and the coordinates attached — ready for you to triage, or for your agent to go fix.
Free for the first 1,000 accounts. No card, no trial clock.