Developers
Noteloop API reference
A small, predictable REST API. Send and read feedback, manage projects, and power a public roadmap. Everything speaks JSON over HTTPS.
Base URL
All endpoints are served from the /api path of your Noteloop installation. On the hosted instance that is:
https://noteloop.dev/apiRequests and responses are application/json (except binary uploads, which are sent as base64 data URLs inside the JSON body). All timestamps are ISO 8601 UTC strings. Self-hosted? Swap the host for your own domain.
Authentication
Noteloop has two authentication schemes:
For sending feedback and reading your widget config from untrusted clients (browser widget, mobile, server-to-server). Send it in the x-api-key header.
x-api-key: ntl_xxxxxxxx…Keys are public-by-design and scoped to a single project. Create them in Dashboard → Project settings → API keys.
For dashboard/management calls tied to a user account. Obtain a JWT from /api/auth/login and send it as a bearer token.
Authorization: Bearer <jwt>Project-scoped endpoints additionally require an x-project-id header identifying which project you act on. You must be a member of that project.
Rate limits & errors
Ingest requests are rate-limited per API key. When you exceed the limit you receive a 429 with a Retry-After header (seconds). Errors always return a JSON body of the shape { "error": "message" }.
| Status | Meaning |
|---|---|
200 / 201 | Success |
204 | Success, no content (deletes) |
400 | Validation failed / missing project context |
401 | Missing or invalid credentials |
403 | Authenticated but not allowed (wrong role, disabled feature) |
404 | Resource not found |
409 | Conflict (e.g. email already registered) |
429 | Rate limited — see Retry-After |
Ingest API — submit feedback
The single most important endpoint. This is what the widget calls, and what you call if you build your own reporter. Authenticated with an API key.
/api/feedbacksauth: API keyOnly description and metadata are required — everything else is optional. Anonymous submissions (no email) are allowed.
Body fields
| Field | Type | Notes |
|---|---|---|
description | string | Required. 1–10 000 chars — the feedback message. |
metadata | object | Required. { userAgent, url, timestamp, viewport?, customData? } |
email | string? | Reporter email. Omit or send "" for anonymous. |
title | string? | ≤200 chars. Auto-derived / AI-generated when omitted. |
screenshot | string? | Base64 data URL (image/png). |
audio | string? | Base64 data URL of a voice note. |
consoleLogs | array? | ≤200 { level, message, timestamp, stack? } |
networkLogs | array? | ≤100 { method, url, status, durationMs, timestamp, error? } |
breadcrumbs | array? | ≤50 { type: click|input|navigate, label, timestamp } |
targetElement | object? | { selector, label, rect? } — the marked broken element. |
identifiedUser | object? | { id?, email?, name?, attributes? } — who the reporter is. |
Example request
curl -X POST https://noteloop.dev/api/feedbacks \
-H "x-api-key: ntl_xxxxxxxx…" \
-H "Content-Type: application/json" \
-d '{
"description": "The export button does nothing on Safari.",
"email": "user@example.com",
"metadata": {
"userAgent": "Mozilla/5.0 …",
"url": "https://app.example.com/reports",
"timestamp": 1782900000000,
"viewport": { "width": 1440, "height": 900 }
}
}'Example response — 201
{ "id": "f1e2d3c4-…" }Widget configuration
The widget fetches its per-project styling/behaviour at init. Authenticated with an API key; you rarely call this yourself.
/api/widget-configauth: API keyReturns the merged config (primary color, launcher position, tooltip, locale and the capture toggles for voice / breadcrumbs / console / network). The dashboard is the source of truth — edit it under Project settings → Widget.
Auth API
Account endpoints. Registration may be disabled on an instance (ALLOW_REGISTRATION=false) once the first admin exists.
/api/auth/registerauth: noneBody { email, password (≥8), name? } → { token, user, defaultProjectId }.
/api/auth/loginauth: noneBody { email, password } → { token, user }. Use the token as your bearer credential.
/api/auth/meauth: BearerUpdate your profile. Changing email/password requires currentPassword. Returns a fresh { token, user }.
Management API — read & triage
Project-scoped endpoints. All require a Bearer token plus the x-project-id header.
/api/feedbacksauth: Bearer + projectList & filter feedback. Query params: status (open·in_progress·resolved·archived), priority, category, assigneeId (none = unassigned), search, tagId, page, pageSize (≤100). Returns { items, total, page, pageSize }.
/api/feedbacks/:idauth: Bearer + project/api/feedbacks/:idauth: Bearer + projectUpdate status, priority, assigneeId, category or tags.
/api/feedbacks/:id/similarauth: Bearer + projectPossible duplicates (word-overlap heuristic).
Comments
/api/comments/:feedbackIdauth: Bearer + project/api/comments/:feedbackIdauth: Bearer + projectBody { body, internal? } — body is 1–5 000 chars; internal marks it as a team-only note.
/api/comments/:feedbackId/:idauth: Bearer + projectAPI keys
/api/keysauth: Bearer + project/api/keysauth: Bearer (owner/admin)Creates a key — the full ntl_… value is returned once on creation; afterwards only a masked form is shown.
/api/keys/:idauth: Bearer (owner/admin)Public roadmap API
Public, unauthenticated endpoints for a project that has enabled its roadmap. Drives the /roadmap/:slug page.
/api/roadmap/:slugauth: noneReturns items bucketed into planned / inProgress / done, each with an upvotes count.
/api/roadmap/:slug/:feedbackId/voteauth: noneBody { voterToken } (8–100 chars, generated & stored client-side). Votes are de-duplicated per token.
Prefer the drop-in widget?
You don't need to touch the API to collect feedback. Drop the widget on any page and it handles screenshots, annotations, console/network capture and submission for you:
<script>
window.noteloopConfig = { apiKey: "ntl_xxxxxxxx…" };
</script>
<script src="https://noteloop.dev/widget/noteloop-widget.js" defer></script>Questions? Contact us — we're happy to help you integrate.