Docs
REST API
Endpoints, authentication, error envelope, pagination and rate limits.
The REST API powers the dashboard, hosted portal and widget. It is versioned under /api/v1. Public API tokens for server-to-server use are coming soon; today, workspace endpoints use your signed-in dashboard session.
Authentication
- Dashboard API (
/api/v1/…): secure session cookie plus theX-CSRF-TOKENheader on writes. Every workspace route requires membership and the right role. - Widget API (
https://ext0.com/w/api/v1/…): short-lived bearer token from a guest or identified widget session. It can never perform staff actions.
Response envelope
{
"data": { "id": "01JEXAMPLEULID", "title": "Dark mode" },
"meta": { "request_id": "req_…" }
}
Errors
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Please correct the highlighted fields.",
"fields": { "title": ["Title is required."] }
},
"meta": { "request_id": "req_…" }
}
| Status | Code | Meaning |
|---|---|---|
| 401 | UNAUTHENTICATED, IDENTITY_REQUIRED | Sign-in or visitor verification needed |
| 402 | PLAN_LIMIT | Your plan does not include this, or a quota is reached |
| 403 | FORBIDDEN | Your role cannot perform this action |
| 404 | NOT_FOUND | Missing — or hidden from you |
| 409 | CONFLICT | State conflict or duplicate idempotency key in progress |
| 422 | VALIDATION_ERROR | Field errors in fields |
| 429 | RATE_LIMITED | Slow down and retry later |
Pagination and idempotency
List endpoints accept limit (max 100) and return a next_cursor; pass it back as cursor. Sensitive writes such as checkout accept an Idempotency-Key header; repeating a key replays the original response.
Endpoints
Account
POST/api/v1/auth/signupPOST/api/v1/auth/loginPOST/api/v1/auth/logoutPOST/api/v1/auth/verify-emailPOST/api/v1/auth/forgot-passwordPOST/api/v1/auth/reset-passwordGET/api/v1/meGET/api/v1/workspacesPOST/api/v1/workspacesGET/api/v1/workspaces/{workspaceId}PATCH/api/v1/workspaces/{workspaceId}GET/api/v1/workspaces/{workspaceId}/membersPOST/api/v1/workspaces/{workspaceId}/invitesDELETE/api/v1/workspaces/{workspaceId}/members/{memberId}GET|PATCH/api/v1/workspaces/{workspaceId}/settings
Feedback
GET/api/v1/workspaces/{workspaceId}/feedbackGET|PATCH/api/v1/workspaces/{workspaceId}/feedback/{feedbackId}POST/api/v1/workspaces/{workspaceId}/feedback/{feedbackId}/mergePOST/api/v1/workspaces/{workspaceId}/feedback/{feedbackId}/commentsGET/api/v1/workspaces/{workspaceId}/moderationPOST/api/v1/workspaces/{workspaceId}/moderation/{itemId}/approve|rejectGET|POST/api/v1/workspaces/{workspaceId}/categories
Roadmap & changelog
GET/api/v1/workspaces/{workspaceId}/roadmapPOST/api/v1/workspaces/{workspaceId}/roadmap/itemsPATCH/api/v1/workspaces/{workspaceId}/roadmap/items/{itemId}POST/api/v1/workspaces/{workspaceId}/roadmap/items/{itemId}/feedback-linksGET|POST/api/v1/workspaces/{workspaceId}/changelogPATCH/api/v1/workspaces/{workspaceId}/changelog/{entryId}POST/api/v1/workspaces/{workspaceId}/changelog/{entryId}/publish
Surveys & analytics
GET|POST/api/v1/workspaces/{workspaceId}/surveysPATCH/api/v1/workspaces/{workspaceId}/surveys/{surveyId}POST/api/v1/workspaces/{workspaceId}/surveys/{surveyId}/publish|closeGET/api/v1/workspaces/{workspaceId}/surveys/{surveyId}/responses|reportGET/api/v1/workspaces/{workspaceId}/analytics/overview
Billing & widget
GET/api/v1/workspaces/{workspaceId}/billingPOST/api/v1/workspaces/{workspaceId}/billing/checkoutPOST/api/v1/workspaces/{workspaceId}/billing/portal-sessionGET/api/v1/workspaces/{workspaceId}/usageGET/api/v1/workspaces/{workspaceId}/widget/installPATCH/api/v1/workspaces/{workspaceId}/widget/configPOST/api/v1/workspaces/{workspaceId}/widget/rotate-identity-secret
Widget (public)
GET/w/api/v1/config/{publicWorkspaceKey}POST/w/api/v1/sessions/guest|identify|logoutGET|POST/w/api/v1/{key}/feedbackGET/w/api/v1/{key}/feedback/{feedbackId}PUT|DELETE/w/api/v1/{key}/feedback/{feedbackId}/votePOST/w/api/v1/{key}/feedback/{feedbackId}/comments|subscribeGET/w/api/v1/{key}/roadmapGET/w/api/v1/{key}/changelog[/{entryId}]GET/w/api/v1/{key}/surveys[/{surveyId}]POST/w/api/v1/{key}/surveys/{surveyId}/impressions|responses
Workspace ids in URLs are public ULIDs. Numeric internal ids are never exposed.