EnoughStack
Feedback

Listen to customers, plan what comes next and share what shipped.

Feedback API

Posts, votes, comments and changelog entries from your own code: open a post when a customer asks for something, vote on their behalf, and listen for post events with webhooks.

Getting started

Create posts and votes for the people using your product, and let webhooks tell you when something ships.

Base URL and authentication
https://enoughstack-app-7r9ib.ondigitalocean.app/api/public/v1
Authorization: Bearer ms_live_…   # create a key under API keys
Log a request from your support tool
curl -X POST https://enoughstack-app-7r9ib.ondigitalocean.app/api/public/v1/feedback/posts \
  -H "Authorization: Bearer $ENOUGHSTACK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"board_id": "features", "title": "Export to Final Cut",
       "details": "Asked twice this week in support.",
       "author": {"external_id": "cus_42", "email": "maya@northwind.studio", "name": "Maya"}}'

The author becomes a portal user (or is found by external id or email), votes for the post and follows it, so they hear when its status changes. Posting the same user again never duplicates them.

Add a vote when a customer asks again
curl -X POST https://enoughstack-app-7r9ib.ondigitalocean.app/api/public/v1/feedback/posts/post_0002/votes \
  -H "Authorization: Bearer $ENOUGHSTACK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"author": {"email": "jonas@fjordlabs.io"}, "note": "Blocking their upgrade"}'

Subscribe a webhook to post.status_changed and changelog.published to close the loop in your own tools.

Boards, statuses and tags

  • GET/feedback/boardsYour feedback boards with post countsread
  • GET/feedback/statusesThe workspace's post statusesread
  • GET/feedback/tagsPost tagsread
  • GET/feedback/changelog/labelsChangelog labelsread

Posts

  • GET/feedback/postsPage through posts; filter by board, status, tag, search or updated_sinceread
  • POST/feedback/postsCreate a post, optionally for one of your userswrite
  • GET/feedback/posts/{post_id}One postread
  • PATCH/feedback/posts/{post_id}Change its title, details, status, tags, owner or ETAwrite
  • DELETE/feedback/posts/{post_id}Delete a post (restorable from the dashboard)write
  • POST/feedback/posts/{post_id}/votesAdd a vote for a userwrite
  • DELETE/feedback/posts/{post_id}/votes/{vote_id}Remove a votewrite
  • GET/feedback/posts/{post_id}/commentsComments; add ?internal=true for team notesread
  • POST/feedback/posts/{post_id}/commentsComment as your team or as a userwrite

Portal users

  • GET/feedback/usersPeople who posted, voted or were identifiedread
  • GET/feedback/users/{user_id}One user with their countersread
  • POST/feedback/users/identifyCreate or update a user by external id or emailwrite

Changelog

  • GET/feedback/changelog/entriesPublished entries; add ?include_drafts=trueread
  • POST/feedback/changelog/entriesCreate an entry; publish_at publishes or schedules itwrite
  • POST/feedback/changelog/entries/{entry_id}/publishPublish a draft nowwrite
  • GET/feedback/changelog/subscribersEmail subscribersread
  • POST/feedback/changelog/subscribersAdd a confirmed subscriberwrite

API keys

Every request carries a key with the read or write scope. Keys are created under Developers and shown once.

Manage API keys

Interactive docs

Every endpoint with request and response schemas, generated from the API itself. Use “Authorize” with one of your keys to try requests.

Open API docs

Conventions

Errors
Always {"error": {"code", "message"}}. Codes include unauthenticated, insufficient_scope, not_found, validation_error.
Pagination
Lists return paging.next; pass it back as starting_after. Up to 100 items per page.
Webhooks
Each delivery carries X-EnoughStack-Signature: sha256=…, an HMAC of the raw body with the endpoint secret. Respond 2xx within 10 seconds.
Versioning
The version is in the path (/v1). Fields are only ever added.

Webhook payload

POST https://your-app.example/hooks
X-EnoughStack-Signature: sha256=…

{
  "event": "contact.created",
  "created_at": "2026-10-06T09:14:02Z",
  "data": {
    "id": "ct_0231",
    "list_id": "lst_audience",
    "email_address": "maya@northwind.studio",
    "status": "subscribed",
    "tags": ["customer", "team"]
  }
}