API documentation
Supoid REST API v1
Programmatic access to your workspace. Available on Growth and Business plans.
Overview
The Supoid API is a REST-over-HTTPS API. All endpoints sit under https://supoid.com/api/public/v1/. Every request is authenticated with a Bearer token, returns JSON, and is rate limited per API key.
- Base URL:
https://supoid.com/api/public/v1 - Auth:
Authorization: Bearer sup_… - Content type:
application/json - Errors: standard envelope (see below)
- OpenAPI 3.1 spec: /api/public/v1/openapi
Authentication
Generate a key in Settings → Integrations → API keys. Each key carries one or more scopes (feedback:read, feedback:write, etc.) and an optional expiration. Keys are shown once — copy them immediately.
curl https://supoid.com/api/public/v1/feedback \ -H "Authorization: Bearer sup_..."
Scopes
feedback:read— list, getfeedback:write— create, update, deleteclusters:readroadmap:read/roadmap:writechangelog:read/changelog:writewebhooks:manage
Errors
Every error returns the same envelope:
{
"error": {
"code": "invalid_request",
"message": "Validation failed",
"details": [...]
}
}Common codes:
missing_authorization— no Bearer headerinvalid_key/expired_keyinsufficient_scope— key lacks the required scopeplan_no_api— workspace plan excludes API accessquota_exceeded— feedback ingest blocked (HTTP 402)rate_limited— slow down (HTTP 429)not_found— resource doesn't exist or isn't yoursinvalid_request— Zod validation failed
Rate limits
Per API key, sliding-window:
- Growth: 60 req/min
- Business: 600 req/min
Responses include X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Resetheaders. On 429 you also get Retry-After in seconds.
Feedback
The customer-facing feedback record. Source is auto-set to api.
Create feedback
POST /api/public/v1/feedback
{
"title": "Add dark mode toggle",
"body": "Late-night users want a dark theme preference saved per workspace.",
"authorEmail": "user@example.com",
"authorName": "Maya"
}Returns 201 Created with the new feedback object.
List feedback
GET /api/public/v1/feedback?status=open&limit=50&cursor=<uuid>
Cursor pagination — the response includes nextCursor; pass it back to fetch the next page. Sorted newest first.
Update / delete
PATCH /api/public/v1/feedback/{id}
DELETE /api/public/v1/feedback/{id}DELETE soft-deletes (the row is hidden from the dashboard and public board). For GDPR hard-delete, contact support.
Clusters
AI-grouped feedback. Read-only via API — clusters are managed by the embedding pipeline.
GET /api/public/v1/clusters
GET /api/public/v1/clusters/{id}Roadmap
GET /api/public/v1/roadmap
POST /api/public/v1/roadmap
GET /api/public/v1/roadmap/{id}
PATCH /api/public/v1/roadmap/{id}
DELETE /api/public/v1/roadmap/{id}Setting status: completed automatically sets completedAt. Pass position to reorder inside a column.
Changelog
GET /api/public/v1/changelog
POST /api/public/v1/changelog
GET /api/public/v1/changelog/{id}
PATCH /api/public/v1/changelog/{id}
DELETE /api/public/v1/changelog/{id}body is markdown. Setting isPublished: true stamps publishedAt server-side and triggers email + webhook fan-out.
Webhooks
Subscribe to events on your own server. Configure in the dashboard or via the API. Each webhook gets a unique signing key — shown once at creation.
Events
feedback.createdfeedback.updatedfeedback.status_changedfeedback.deletedcomment.addedvote.createdcluster.summarisedroadmap.created/roadmap.updatedchangelog.published
Payload envelope
{
"event": "feedback.created",
"data": { "workspaceId": "...", "feedbackId": "..." },
"timestamp": 1735689600
}Headers
X-Supoid-Event— event nameX-Supoid-Webhook-Id— webhook uuidX-Supoid-Signature—t=<unix>,v1=<hex hmac sha256>User-Agent—Supoid-Webhook/1.0
Verifying signatures (Node.js)
import crypto from "node:crypto";
export function verifySupoidSignature(opts) {
const { rawBody, header, signingKey } = opts;
const [t, v1] = header.split(",").map(p => p.split("=")[1]);
const expected = crypto
.createHmac("sha256", signingKey)
.update(`${t}.${rawBody}`)
.digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected))) {
throw new Error("Invalid signature");
}
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) {
throw new Error("Stale signature (>5min)");
}
}Retries + auto-disable
Each delivery has a 10-second timeout. On non-2xx or network error, Inngest retries up to 5 times with exponential backoff. After 10 consecutive failures the webhook is auto-disabled and an audit log entry is written — re-enable it from the dashboard.
OpenAPI spec
The full machine-readable spec lives at /api/public/v1/openapi. Drop it into Swagger Editor, Insomnia, Postman, or your codegen of choice.
Found a bug or want a new endpoint? Email hello@supoid.com.