Docs

Everything public about a project lives under one slug. Replace your-project with yours.

Every widget on this page is live — this page loads the same embed.js you would, pointed at our demo project. Click things.

Your public hub

Every project gets a hub page with a tab per enabled module. Turn a module off in Settings and its tab disappears.

https://feedfa.st/p/your-project
Changelog — the hub's default tab
https://feedfa.st/p/your-project/feedback
Feedback board
https://feedfa.st/p/your-project/status
Status page
https://feedfa.st/p/your-project/changelog/entry-slug
A single changelog entry

Install the script

One script tag, then place widgets wherever you want them. Every widget renders inside a shadow root, so your CSS and ours can never collide.

<script src="https://feedfa.st/embed.js" data-project="your-project" defer></script>
data-project
Required. Your project slug.
data-theme
auto (default) · light · dark
data-accent
Override the accent colour, e.g. #4F46E5
data-badge
false to turn the floating badge off
data-position
bottom-right (default) · bottom-left
data-user-id / -email / -name
Identify the signed-in user — see below
Widgets added later — by a router, a modal, an accordion — are picked up automatically, so this works in a SPA without extra wiring.

Feedback button

Put data-feedfast="feedback" on any element — a button, a link, a menu item. It opens a modal that posts straight to your board.

If the element already has content, we leave your markup and styling completely alone and only attach the click handler.

<button class="my-button" data-feedfast="feedback">
Send feedback
</button>
Live · this page's own CSS

The modal asks for a category, title, optional details and an optional email — the same fields as the public board, with the same rate limiting and spam trap. The submitted idea starts with the author's own upvote.

Identify your users

When the button lives inside your dashboard, you already know who's clicking it. Pass them along and the modal drops its name and email fields entirely.

No setup. Whatever your page sends is accepted and stored, marked unverified.

<button
data-feedfast="feedback"
data-user-id="${user.id}"
data-user-email="${user.email}"
data-user-name="${user.name}"
>Send feedback</button>
Live · prefilled identity
Open it — no name or email to fill in.

This is only asserted, not verified

The widget endpoint is public and unauthenticated, so on its own an identity is just a claim made by whatever page sent it — someone could call the endpoint directly as anyone. Fine for an internal tool. When the author matters, use the verified tab.
data-user-id
Your own user id. Opaque to us, never shown publicly.
data-user-email
Prefills the author, and makes their votes portable.
data-user-name
Shown as “Posting as …” in the modal.
data-user-hash
HMAC-SHA256 of the user id. Required once a secret is set.

Set the same attributes on the loader script to cover every trigger on the page, or call Feedfast.identify(...) once your SPA has the session. Identity on a trigger beats identity on the loader, so one page can have both an authenticated and an anonymous entry point.

Feedfast.identify({ id: user.id, email: user.email, name: user.name });
Feedfast.identify(null); // back to anonymous on sign-out

Uptime bar

The same 90-day strip as your status page, at any size.

<div data-feedfast="uptime"></div>
Live
data-monitor
Show a single monitor, by name
data-days
1–90. How much history to draw
data-compact
true | false — shorter bars
data-overall
true | false — the status line above the monitors

Which day is a bar?

Days are bucketed in your project's timezone, not the visitor's — the payload is shared by every visitor and cached, so it has to be zone-stable. Hovering a bar shows the date in the visitor's own locale.

Changelog

Two shapes. Cards for a landing page or a dedicated “what's new” section; inline for a compact list in a sidebar or footer.

<div data-feedfast="changelog" data-variant="card" data-limit="3"></div>
Live
data-variant
card (default) | inline
data-limit
1–10. How many entries
data-excerpt
true | false — card variant only
data-tags
true | false — show tag pills

Floating badge

On by default: a “What's new” badge pinned to a corner, with an unread dot that clears once the visitor opens it.

<!-- on by default -->
<script src="https://feedfa.st/embed.js" data-project="your-project" defer></script>

<!-- off, if you'd rather place widgets yourself -->
<script src="https://feedfa.st/embed.js" data-project="your-project" data-badge="false" defer></script>
Live · the badge panel

These docs load the script with data-badge="false", so the badge isn't pinned over the page — this button opens the same panel it would.

The unread state lives in the visitor's own browser. If maintenance is scheduled, a notice appears above the entries.

JavaScript API

The loader exposes window.Feedfast once it runs.

Feedfast.openFeedback(opts?)
Open the modal. { categoryId, title, subtitle, user }
Feedfast.openChangelog()
Open the “What's new” panel
Feedfast.identify(user)
Set the signed-in user; null clears it
Feedfast.close()
Close whatever is open
Feedfast.refresh()
Re-fetch and re-render every widget
Feedfast.mount(root?)
Scan for new widgets (usually automatic)
Feedfast.data()
The current payload, or null before first load

RSS feeds

Both feeds are RSS 2.0 with content:encoded, cached for five minutes.

https://feedfa.st/p/your-project/rss.xml
Published changelog entries
https://feedfa.st/p/your-project/status.rss
Incidents and maintenance windows

JSON payload

The widgets read one small public document. It's CORS-open and cached for 60 seconds, so you can build your own UI on top of it.

GET https://feedfa.st/api/widget/your-project
No auth required
{
"project": { "name": "Acme Analytics", "slug": "acme", "accentColor": "#4F46E5" },
"hubUrl": "https://feedfa.st/p/acme",
"modules": { "changelog": true, "feedback": true, "status": true },
"entries": [{ "id": "", "title": "", "url": "", "excerpt": "",
"tags": ["New"], "publishedAt": "2026-08-14T05:12:59.305Z" }],
"categories": [{ "id": "", "name": "Bug", "color": "#EF4444", "isDefault": false }],
"uptime": {
"overall": { "state": "operational", "label": "All systems operational" },
"monitors": [{ "name": "API", "state": "up", "uptimePct": 99.82,
"bars": "uuuuuuuuduuu…" }],
"days": 90, "startDay": "2026-05-19", "timezone": "Europe/Bucharest"
},
"maintenance": { "title": "Database upgrade", "status": "SCHEDULED",
"startsAt": "2026-08-19T08:41:12.638Z",
"endsAt": "2026-08-19T09:41:12.638Z" }
}

Each character in bars is one day, oldest first: u up, d down, g degraded, m maintenance, n no data. Every timestamp is ISO 8601 in UTC.

Board filters

Feedback board filters live in the URL, so any view you can see is a link you can share.

?category=bug
One category, by slug
?category=bug,feature-request
Several categories
?status=open,planned
Filter by status
?sort=top | new | trending
Top votes (default), newest, or votes in the last 7 days
?q=slack
Free-text search across title and body

Status subscribers

Anyone can subscribe from your status page, with no account.

Subscriptions are double opt-in — the address only receives mail after the confirmation link is clicked — and every email carries a one-click unsubscribe link.

Subscribers are emailed when an incident opens or resolves, and at whichever maintenance milestones you enabled (scheduled, started, completed).

API keys

The REST API and the MCP server both authenticate with a bearer key from Settings → Account.

Keys look like ff_…. Only a SHA-256 hash is stored, so the plaintext is shown exactly once, at creation.

curl https://feedfa.st/api/v1/projects \
-H "Authorization: Bearer ff_your_key_here"
Scope
READ_ONLY or READ_WRITE — writes 403 on a read-only key
Rate limit
120 requests per minute per key
Reach
Every project on the account that owns the key

REST API

Everything the dashboard can do to a project, over HTTP.

GET /api/v1/projects
Every project this key can reach
GET /api/v1/projects/:slug/changelog
?status=&limit=
POST /api/v1/projects/:slug/changelog
Create, and publish by default
PATCH /api/v1/projects/:slug/changelog
Edit, publish or unpublish
GET /api/v1/projects/:slug/feedback
?category=&status=&sort=&q=&limit=
GET /api/v1/projects/:slug/feedback/:postId
One post with its comment thread
PATCH /api/v1/projects/:slug/feedback
Status, category, pin, owner reply
POST /api/v1/projects/:slug/feedback/:postId/resolve
Close it, reply, and publish the changelog entry in one transaction
GET /api/v1/projects/:slug/uptime
?days= — state, uptime windows, incidents
GET /api/v1/projects/:slug/maintenance
?upcoming=true
POST /api/v1/projects/:slug/maintenance
Schedule a window
PATCH /api/v1/projects/:slug/maintenance
start | complete | cancel

Two ways to say when

Maintenance times accept either a wall-clock string read in an explicit timezone ("2026-09-01T02:00") or a full ISO instant ("2026-08-31T23:00:00Z"). The first is what a person would say; the second is what a machine already has.
curl -X POST https://feedfa.st/api/v1/projects/acme/maintenance \
-H "Authorization: Bearer ff_…" \
-H "Content-Type: application/json" \
-d '{
"title": "Database upgrade",
"startsAt": "2026-09-01T02:00",
"endsAt": "2026-09-01T03:30",
"timezone": "Europe/Bucharest",
"monitors": ["API"]
}'

MCP server

Connect Claude or any MCP client and let it publish release notes, schedule maintenance, and read your board and uptime.

{
"mcpServers": {
"feedfast": {
"command": "npx",
"args": ["-y", "feedfast-mcp"],
"env": {
"FEEDFAST_API_KEY": "ff_your_key_here",
"FEEDFAST_URL": "https://feedfa.st"
}
}
}
}
list_projects
Slugs, enabled modules and content counts
get_uptime
Monitor state, uptime windows, open and recent incidents
list_feedback
Filter by status, category, search; sort by top/new/trending
get_feedback
One post with its full comment thread
update_feedback
Status, category, pin, and public owner replies
resolve_feedback
Close the loop: mark it done, reply publicly, publish the changelog entry, link them
list_changelog
Published and draft entries
publish_changelog
Write a release note — public immediately
update_changelog
Edit, publish a draft, or unpublish
list_maintenance
Scheduled, in progress and completed windows
schedule_maintenance
Announce planned downtime
transition_maintenance
Start now, complete early, or cancel

It can only do what the key can

The MCP server holds no database connection — it's a client of the REST API, so a key grants exactly the same access through an agent as it would through curl. Issue a read-only key if you only want the agent to answer questions.

Want to see it all working? Browse the live demo hub.