Base URL
Use your deployment host plus /v1. This deployment uses https://www.yoofi.app/v1. Local development usually uses http://localhost:3000/v1.
Yoofi exposes versioned REST endpoints for public portal reads, authenticated idea workflows, and admin-scoped workspace queries.
Use your deployment host plus /v1. This deployment uses https://www.yoofi.app/v1. Local development usually uses http://localhost:3000/v1.
Routes under /api power the web app and can change without notice. Treat /v1 as the stable integration surface.
The seed script creates a demo portal at /portal/public and an API key named yoofi_dev_key_123456789 for local development. Deployed workspaces can create scoped API keys from the Admin Console.
curl "https://www.yoofi.app/v1/portals/public/ideas?status=PLANNED&sort=top"curl -H "Authorization: Bearer YOOFI_ADMIN_KEY" \
"https://www.yoofi.app/v1/workspace?portalSlug=public&status=PLANNED&limit=10"curl -X POST "https://www.yoofi.app/v1/portals/public/ideas" \
-H "Authorization: Bearer yoofi_dev_key_123456789" \
-H "Content-Type: application/json" \
-d '{
"title": "Add dark mode for portal pages",
"description": "Customers need a dark theme for public portal browsing on low-light devices.",
"categoryId": null,
"tagIds": []
}'curl -X POST "https://www.yoofi.app/v1/ideas/IDEA_ID/vote" \
-H "X-API-Key: yoofi_dev_key_123456789"{
"items": [
{
"id": "cm9exampleidea",
"title": "Publish webhook retries with delivery logs",
"description": "Expose webhook retry history so integration teams can debug failures without support tickets.",
"status": "PLANNED",
"targetTimeframe": "Q2 2026",
"category": {
"id": "cm9category",
"name": "Integrations",
"slug": "integrations"
},
"tags": [
{
"tag": {
"id": "cm9tag",
"name": "API",
"slug": "api"
}
}
],
"_count": {
"votes": 1,
"follows": 1
}
}
],
"page": 1,
"pageSize": 10,
"total": 1,
"totalPages": 1
}Authenticated workspace and mutation endpoints accept either a browser session or an API key.
Use the same authenticated browser session as the web app. This is the easiest option for in-product integrations.
Send an API key in the Authorization header as Bearer <key>.
If you cannot set Authorization, send the same key in X-API-Key.
Workspace admins can mint scoped keys from the Admin Console for server-to-server access.
Yoofi checks scopes on authenticated workspace and mutation routes.
public:readRead access for public resources. Current GET /v1 routes do not require authentication, but this scope is included on session and API-key principals.
ideas:writeRequired for POST /v1/portals/:portalSlug/ideas.
votes:writeRequired for vote, unvote, follow, and unfollow operations.
workspace:readRequired for GET /v1/workspace. This scope is available only on admin sessions and admin-created API keys.
admin:writeReserved for future admin mutation routes and restricted to admin-created API keys.
A few behaviors are shared across the public API.
| Topic | Value | Details |
|---|---|---|
| Base path | /v1 | Public, versioned REST routes live under /v1. Internal web-app routes under /api are not part of the public contract. |
| Content type | application/json | Write routes expect JSON request bodies. Responses are JSON for both success and error cases. |
| Pagination | page | The ideas list accepts page. The current page size is fixed at 10 items per page. |
| Idea statuses | UNDER_REVIEW, PLANNED, COMPLETED | Use these exact enum values when filtering or interpreting roadmap results. |
| Idea sort values | top, new, trending | Invalid sort values fall back to top. |
| Admin workspace query | workspace:read | GET /v1/workspace is scoped to the authenticated workspace and supports admin sessions or admin-created keys. |
The details below match the current route handlers in the app.
Query the authenticated workspace, including products, taxonomy, recent announcements, and filtered ideas.
| Name | Type | Required | Description |
|---|---|---|---|
portalSlug | string | No | Optional product slug to limit ideas and announcements to one portal. |
q | string | No | Optional search term applied to idea title and description. |
status | UNDER_REVIEW | PLANNED | COMPLETED | No | Optional idea status filter. |
limit | number | No | Maximum number of idea records returned. Defaults to 25 and caps at 100. |
List ideas for a portal with optional search, filtering, sorting, and pagination.
| Name | Type | Required | Description |
|---|---|---|---|
q | string | No | Search title and description. |
status | UNDER_REVIEW | PLANNED | COMPLETED | No | Filter by idea status. |
tag | string | No | Filter by tag slug. |
category | string | No | Filter by category slug. |
sort | top | new | trending | No | Ordering mode. Defaults to top. |
page | number | No | 1-based page number. Values below 1 are treated as 1. |
Create a new idea inside a portal that belongs to the authenticated workspace.
| Name | Type | Required | Description |
|---|---|---|---|
title | string | Yes | 5-140 characters. |
description | string | Yes | 10-5000 characters. |
categoryId | string | null | No | Optional category id from the same workspace. |
tagIds | string[] | No | Optional tag ids from the same workspace. |
Fetch a single idea record, including counts, tags, category, and the latest idea events.
Vote for an idea. Voting also ensures the caller follows the idea.
Remove the caller vote from an idea.
Follow an idea without affecting its vote count.
Stop following an idea.
List active tags for a workspace.
| Name | Type | Required | Description |
|---|---|---|---|
workspace | string | No | Workspace slug. If omitted, the first workspace in the database is used. |
List published announcements for a portal.
| Name | Type | Required | Description |
|---|---|---|---|
portalSlug | string | No | Portal slug. If omitted, the first public portal is used. |
Return the current roadmap buckets for a portal.
| Name | Type | Required | Description |
|---|---|---|---|
portalSlug | string | No | Portal slug. If omitted, the first public portal is used. |
GET /v1/ideas/:ideaId returns the idea record plus merge metadata.
{
"item": {
"id": "cm9exampleidea",
"title": "Add advanced trend filters on analytics dashboard",
"status": "UNDER_REVIEW",
"canonicalIdeaId": null,
"category": {
"id": "cm9category",
"name": "User Experience",
"slug": "ux"
},
"tags": [
{
"tag": {
"id": "cm9tag",
"name": "Analytics",
"slug": "analytics"
}
}
],
"_count": {
"votes": 2,
"follows": 2
},
"events": [
{
"id": "cm9event",
"type": "CREATED",
"payload": "{\"message\":\"Idea submitted\"}"
}
]
},
"canonicalIdeaId": null,
"merged": false
}Public API errors use a consistent JSON envelope.
{
"code": "validation_failed",
"message": "Title must contain at least 5 character(s)",
"requestId": "0f5b5cc4-5db1-40bb-9f74-df0d497f0eb4"
}auth_failed: Missing session, invalid API key, or missing required scope.portal_not_found: Unknown portal slug on list or create routes.workspace_not_found: Authenticated workspace no longer exists.idea_not_found: Unknown, deleted, merged, or out-of-workspace idea for the current route.validation_failed: Body parsing failed or a field failed validation.forbidden: Authenticated principal tried to write into a different workspace.