Feedback API
Request features, report issues and upvote what matters to you. 14 ops: list_feedback, get_feedback, list_my_feedback, request_feature, report_issue, upvote_feedback, remove_feedback_vote, comment_on_feedback, set_feedback_subscription, submit_review, list_reviews, update_feedback, respond_to_feedba
Request features, report issues and upvote what matters to you.
List feature requests and issues · POST /api/v1/list_feedback · scope feedback:read or GET …?input=
The Wabi feedback board: feature requests every customer can see, plus your own issue reports (and ones the team published), with status and upvotes, most upvoted first. Each item says whether your organization voted for it or filed it. Call it before request_feature or report_issue to find an existing item to upvote instead of filing a duplicate, or when the human asks what is planned. Works without a credential (public items only).
curl -X POST https://getwabi.dev/api/v1/list_feedback \ -H "Authorization: Bearer wabi_…"npx @meterapp/wabi list_feedback{
"type": "object",
"properties": {
"kind": {
"default": "all",
"description": "feature = feature requests, issue = bug reports (yours, and ones the team published).",
"type": "string",
"enum": [
"feature",
"issue",
"all"
]
},
"status": {
"default": "active",
"description": "active = open, planned, in_progress.",
"type": "string",
"enum": [
"active",
"all",
"open",
"planned",
"in_progress",
"done",
"declined",
"duplicate"
]
},
"sort": {
"default": "top",
"description": "top = most upvoted, new = most recent, recent_activity = latest vote, comment or status change.",
"type": "string",
"enum": [
"top",
"new",
"recent_activity"
]
},
"q": {
"description": "Words to find in the title.",
"type": "string",
"maxLength": 100
},
"mine": {
"default": false,
"description": "Only items your organization filed.",
"type": "boolean"
},
"limit": {
"default": 20,
"description": "Page size, at most 100.",
"type": "integer",
"minimum": 1,
"maximum": 100
},
"offset": {
"default": 0,
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"cursor": {
"description": "next_cursor from the previous page (same as offset).",
"type": "string",
"pattern": "^\\d+$"
}
}
}Get a feature request or issue · POST /api/v1/get_feedback · scope feedback:read or GET …?input=
One feedback item with its status, upvotes, comments (oldest first; official answers from the Wabi team are marked `official`) and its timeline of status changes with the team's notes. Your own issues include the context you attached.
curl -X POST https://getwabi.dev/api/v1/get_feedback \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"id":1}'npx @meterapp/wabi get_feedback --json '{"id":1}'{
"type": "object",
"properties": {
"id": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991,
"description": "The item's id from list_feedback, request_feature or report_issue (e.g. 42)."
}
},
"required": [
"id"
]
}list_my_feedback
readList my feedback and subscriptions · POST /api/v1/list_my_feedback · scope feedback:read or GET …?input=
What your organization filed (every status), the items you get email about (filed, upvoted, commented, followed) with whether you muted them, your organization's review, and whether feedback email reaches you. Use it to follow up on earlier reports.
curl -X POST https://getwabi.dev/api/v1/list_my_feedback \ -H "Authorization: Bearer wabi_…"npx @meterapp/wabi list_my_feedback{
"type": "object",
"properties": {}
}request_feature
writeRequest a feature · POST /api/v1/request_feature · scope feedback:write
Files a feature request on the Wabi board and upvotes it for your organization. Use it when the human wishes Wabi did something it cannot do yet (an integration, an op, a Meta feature, a dashboard view). It checks for duplicates first: an identical request gets your vote instead, and when likely matches exist nothing is filed and `similar` lists them (filed: false): upvote_feedback the match, or call again with force: true if yours is different. 10 new items per organization a day. The person behind the call gets a thank-you email and one at every status change. Feature requests and comments are public (never who wrote them beyond a first name): no API keys, message content or customer data.
curl -X POST https://getwabi.dev/api/v1/request_feature \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"title":"Show template rejection reasons in the inbox"}'npx @meterapp/wabi request_feature --json '{"title":"Show template rejection reasons in the inbox"}'{
"type": "object",
"properties": {
"title": {
"type": "string",
"minLength": 3,
"maxLength": 140,
"description": "What you want, in one line."
},
"details": {
"default": "",
"description": "Why you need it and what you are building; concrete examples help it get planned.",
"type": "string",
"maxLength": 5000
},
"body": {
"description": "Alias of details.",
"type": "string",
"maxLength": 5000
},
"force": {
"default": false,
"description": "File even when similar items exist.",
"type": "boolean"
}
},
"required": [
"title"
]
}report_issue
writeReport an issue · POST /api/v1/report_issue · scope feedback:write
Reports a problem with Wabi itself: a failing or wrong call (pass the request_id from the error or the X-Request-Id header, and the op), a message that was not delivered or delivered twice, a webhook that never arrived, a template that will not submit, an agent that misbehaved, a docs error. Attach context (account_id, conversation_id, message_id or wamid, template, Meta's error code, the error) so the team can reproduce it; ids are checked against your organization. Issues are private: only your organization and the Wabi team see them, and the team may publish a sanitized title so others can upvote it. Upvote a matching report from list_feedback (kind issue) instead of filing again; likely duplicates are returned in `similar` with filed: false unless force: true. 10 new items per organization a day. The reporter gets an email with what was captured and one at every status change.
curl -X POST https://getwabi.dev/api/v1/report_issue \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"title":"Show template rejection reasons in the inbox"}'npx @meterapp/wabi report_issue --json '{"title":"Show template rejection reasons in the inbox"}'{
"type": "object",
"properties": {
"title": {
"type": "string",
"minLength": 3,
"maxLength": 140,
"description": "What is wrong, in one line."
},
"details": {
"default": "",
"description": "What you did, what you expected and what happened (status, error code, format).",
"type": "string",
"maxLength": 5000
},
"body": {
"description": "Alias of details.",
"type": "string",
"maxLength": 5000
},
"request_id": {
"description": "The request_id of the failing call (shortcut for context.request_id).",
"type": "string",
"maxLength": 128
},
"op": {
"description": "The op the failing call used, e.g. send_message (shortcut for context.op).",
"type": "string",
"maxLength": 80
},
"context": {
"description": "Wabi identifiers for what failed: request_id, op, account_id, conversation_id, message_id, wamid, template, meta_error_code, status, error_code, error, client. Never shown publicly; at most 128 KB.",
"type": "object",
"properties": {
"request_id": {
"description": "The request_id of the failing call (X-Request-Id header, or request_id in the error).",
"type": "string",
"pattern": "^[A-Za-z0-9._:-]{1,128}$"
},
"op": {
"description": "The op or endpoint that failed, e.g. send_message.",
"type": "string",
"pattern": "^[a-z][a-z0-9_]{1,79}$"
},
"account_id": {
"description": "The WhatsApp account (whatsapp_accounts.id) the problem is in; must be one of your organization's.",
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"conversation_id": {
"description": "The conversation involved; must be one of your organization's.",
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"message_id": {
"description": "The message involved (our id, not the wamid); must be one of your organization's.",
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"wamid": {
"description": "Meta's message id (wamid.…) when you have no message_id.",
"type": "string",
"maxLength": 200
},
"template": {
"description": "The template name involved.",
"type": "string",
"maxLength": 512
},
"locale": {
"description": "The language involved, e.g. pt_BR.",
"type": "string",
"pattern": "^[A-Za-z]{2,8}([-_][A-Za-z0-9]{1,8})*$"
},
"meta_error_code": {
"description": "Meta's error code, when Meta returned one (e.g. 131047).",
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"status": {
"description": "The HTTP status the call returned.",
"type": "integer",
"minimum": 100,
"maximum": 599
},
"error_code": {
"description": "The error code, e.g. outside_service_window.",
"type": "string",
"maxLength": 100
},
"error": {
"description": "The error message as the client saw it (no keys or customer strings).",
"type": "string",
"maxLength": 2000
},
"client": {
"description": "Client and version, e.g. wabi-cli/0.5.0 or Claude Code.",
"type": "string",
"maxLength": 100
},
"version": {
"type": "string",
"maxLength": 50
},
"extra": {
"description": "Anything else that helps reproduce it (no secrets).",
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
}
},
"additionalProperties": false
},
"force": {
"default": false,
"description": "File even when similar reports exist.",
"type": "boolean"
}
},
"required": [
"title"
]
}upvote_feedback
writeUpvote a feature request or issue · POST /api/v1/upvote_feedback · scope feedback:write
Adds your organization's upvote to a feature request or issue ("me too"); votes decide what gets built and fixed first, and you get an email when its status changes. Idempotent: one vote per organization. A vote on a duplicate goes to the original. Returns the item with its new vote count.
curl -X POST https://getwabi.dev/api/v1/upvote_feedback \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"id":1}'npx @meterapp/wabi upvote_feedback --json '{"id":1}'{
"type": "object",
"properties": {
"id": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991,
"description": "The item's id from list_feedback, request_feature or report_issue (e.g. 42)."
}
},
"required": [
"id"
]
}remove_feedback_vote
writeRemove an upvote · POST /api/v1/remove_feedback_vote · scope feedback:write
Takes back your organization's upvote on a feature request or issue, and stops its emails unless you filed or commented on it. Returns the item with its new vote count.
curl -X POST https://getwabi.dev/api/v1/remove_feedback_vote \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"id":1}'npx @meterapp/wabi remove_feedback_vote --json '{"id":1}'{
"type": "object",
"properties": {
"id": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991,
"description": "The item's id from list_feedback, request_feature or report_issue (e.g. 42)."
}
},
"required": [
"id"
]
}comment_on_feedback
writeComment on a feature request or issue · POST /api/v1/comment_on_feedback · scope feedback:write
Adds context to an existing feature request or issue: your use case, another failing request, a workaround. Prefer it (with upvote_feedback) over filing a near-duplicate. You then get emails about the item; its author gets a daily digest of new comments. 50 comments per organization a day. Feature requests and comments are public (never who wrote them beyond a first name): no API keys, message content or customer data.
curl -X POST https://getwabi.dev/api/v1/comment_on_feedback \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"id":1,"body":"It would save us a trip to Meta Business Manager."}'npx @meterapp/wabi comment_on_feedback --json '{"id":1,"body":"It would save us a trip to Meta Business Manager."}'{
"type": "object",
"properties": {
"id": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991,
"description": "The item's id from list_feedback, request_feature or report_issue (e.g. 42)."
},
"body": {
"type": "string",
"minLength": 1,
"maxLength": 5000,
"description": "The comment."
}
},
"required": [
"id",
"body"
]
}Start or stop feedback emails · POST /api/v1/set_feedback_subscription · scope feedback:write
With id: start or stop emails about one feature request or issue. Without id: turn every feedback email (thank-yous, status changes, replies) on or off for the person behind this credential. Lifecycle and product emails are separate.
curl -X POST https://getwabi.dev/api/v1/set_feedback_subscription \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"subscribed":false}'npx @meterapp/wabi set_feedback_subscription --json '{"subscribed":false}'{
"type": "object",
"properties": {
"id": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991,
"description": "The item's id from list_feedback, request_feature or report_issue (e.g. 42)."
},
"subscribed": {
"type": "boolean",
"description": "true to get emails, false to stop them."
}
},
"required": [
"subscribed"
]
}submit_review
writePost or edit a review · POST /api/v1/submit_review · scope feedback:write
Posts your organization's public review of Wabi (1–5 stars, what you like, what you don't, what you use it for), or edits it (once an hour; earlier versions are kept). Ask the human for the rating and words; never invent a review or post one they did not approve. It publishes on /reviews right away unless an automated check holds it for a quick look (new accounts, links, profanity). marketing_consent: true lets Meter quote it. The reviewer gets a thank-you email and one when the team replies.
curl -X POST https://getwabi.dev/api/v1/submit_review \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"rating":1}'npx @meterapp/wabi submit_review --json '{"rating":1}'{
"type": "object",
"properties": {
"rating": {
"type": "integer",
"minimum": 1,
"maximum": 5,
"description": "1 (poor) to 5 (excellent)."
},
"title": {
"default": "",
"description": "One-line summary.",
"type": "string",
"maxLength": 120
},
"body": {
"default": "",
"description": "The review.",
"type": "string",
"maxLength": 2000
},
"likes": {
"default": "",
"description": "What works well.",
"type": "string",
"maxLength": 1000
},
"dislikes": {
"default": "",
"description": "What should be better.",
"type": "string",
"maxLength": 1000
},
"use_case": {
"default": "",
"description": "What you use Wabi for.",
"type": "string",
"maxLength": 200
},
"display_name": {
"description": "Name shown with the review; defaults to your first name.",
"type": "string",
"maxLength": 60
},
"display_company": {
"description": "Company shown with the review, if any.",
"type": "string",
"maxLength": 80
},
"marketing_consent": {
"default": false,
"description": "May Meter quote this review on the site and in marketing.",
"type": "boolean"
}
},
"required": [
"rating"
]
}List reviews · POST /api/v1/list_reviews · scope feedback:read or GET …?input=
Published reviews of Wabi with the aggregate (average rating, count and distribution) and the team's responses. Works without a credential.
curl -X POST https://getwabi.dev/api/v1/list_reviews \ -H "Authorization: Bearer wabi_…"npx @meterapp/wabi list_reviews{
"type": "object",
"properties": {
"sort": {
"default": "newest",
"description": "helpful = most detailed first.",
"type": "string",
"enum": [
"newest",
"helpful",
"highest",
"lowest"
]
},
"rating": {
"description": "Only reviews with this rating.",
"type": "integer",
"minimum": 1,
"maximum": 5
},
"status": {
"default": "published",
"description": "Wabi team only: held or hidden reviews too.",
"type": "string",
"enum": [
"published",
"pending",
"hidden",
"all"
]
},
"limit": {
"default": 20,
"type": "integer",
"minimum": 1,
"maximum": 100
},
"offset": {
"default": 0,
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
}
}
}update_feedback
writeUpdate feedback (staff) · POST /api/v1/update_feedback · scope feedback:write
Wabi team only. Moves an item through open → planned → in_progress → done, or declined, or duplicate (with duplicate_of: votes and subscribers move to the original), with a note every subscriber gets by email. Also publishes or hides an issue (visibility, with a sanitized public_title) and links the GitHub issue or PR (external_ref).
curl -X POST https://getwabi.dev/api/v1/update_feedback \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"id":1}'npx @meterapp/wabi update_feedback --json '{"id":1}'{
"type": "object",
"properties": {
"id": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991,
"description": "The item's id from list_feedback, request_feature or report_issue (e.g. 42)."
},
"status": {
"type": "string",
"enum": [
"open",
"planned",
"in_progress",
"done",
"declined",
"duplicate"
]
},
"note": {
"description": "Why, or what shipped and how to use it; emailed with the status change.",
"type": "string",
"maxLength": 5000
},
"duplicate_of": {
"description": "The original, with status duplicate.",
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991
},
"external_ref": {
"description": "GitHub issue or PR URL; empty clears it.",
"anyOf": [
{
"type": "string",
"maxLength": 500,
"format": "uri"
},
{
"type": "string",
"const": ""
}
]
},
"visibility": {
"type": "string",
"enum": [
"public",
"private"
]
},
"public_title": {
"description": "Sanitized title others see for a published issue.",
"type": "string",
"maxLength": 140
}
},
"required": [
"id"
]
}respond_to_feedback
writeRespond officially (staff) · POST /api/v1/respond_to_feedback · scope feedback:write
Wabi team only. Posts an official response on an item (emailed to every subscriber), optionally changing its status in the same call.
curl -X POST https://getwabi.dev/api/v1/respond_to_feedback \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"id":1,"body":"It would save us a trip to Meta Business Manager."}'npx @meterapp/wabi respond_to_feedback --json '{"id":1,"body":"It would save us a trip to Meta Business Manager."}'{
"type": "object",
"properties": {
"id": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991,
"description": "The item's id from list_feedback, request_feature or report_issue (e.g. 42)."
},
"body": {
"type": "string",
"minLength": 1,
"maxLength": 5000,
"description": "The response, signed as the Wabi team."
},
"status": {
"type": "string",
"enum": [
"open",
"planned",
"in_progress",
"done",
"declined",
"duplicate"
]
},
"note": {
"type": "string",
"maxLength": 5000
},
"duplicate_of": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991
},
"external_ref": {
"type": "string",
"maxLength": 500,
"format": "uri"
}
},
"required": [
"id",
"body"
]
}respond_to_review
writeRespond to or moderate a review (staff) · POST /api/v1/respond_to_review · scope feedback:write
Wabi team only. Replies publicly under a review (the reviewer gets an email), and/or hides or publishes it with a reason. Never hide a review for being negative.
curl -X POST https://getwabi.dev/api/v1/respond_to_review \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"id":1}'npx @meterapp/wabi respond_to_review --json '{"id":1}'{
"type": "object",
"properties": {
"id": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991,
"description": "The review id from list_reviews."
},
"response": {
"type": "string",
"minLength": 1,
"maxLength": 2000
},
"status": {
"type": "string",
"enum": [
"published",
"hidden",
"pending"
]
},
"reason": {
"description": "Why it is hidden; kept internal.",
"type": "string",
"maxLength": 300
}
},
"required": [
"id"
]
}