Feedback API

Add a Send feedback button to a website, PWA, or mobile app and collect feature requests, bug reports, and improvements in a cmd-deck project. Your users do not need cmd-deck accounts.

Submissions appear in User Feedback, where you can filter by project and review them. They do not immediately become tasks on the board. Use Convert to task to create a Todo task when you decide to act on a submission.

1. Enable a project and get its feedback key

Each receiving project needs feedback enabled and a dedicated key beginning with fdbk_. This is different from the cmdk_ API key generated on the Connect page. A feedback key permits submission to one project; it does not grant board access.

Project activation currently requires an administrator; there is no self-service feedback settings screen. For hosted setup, contact support with your workspace and project name. If you administer your own cmd-deck database, run this in its Supabase SQL Editor, replacing the UUID with your existing project's ID:

UPDATE public.projects
SET feedback_enabled = true
WHERE id = 'YOUR_PROJECT_UUID'
RETURNING id, name, feedback_api_key;

The database trigger generates the key if the project does not already have one. Keep the returned key in your application's server configuration. Do not post it in issues or documentation. To disable submissions, set feedback_enabled = false. Disabling and re-enabling preserves the key; it does not rotate it.

The key determines the destination project. Sending a project_id in the submission does not override that destination.

2. Send a request

POST https://app.cmddeck.dev/api/feedback
Content-Type: application/json

Authentication is the api_key field in the JSON body, not a bearer token or X-API-Key header. No MCP client or SDK is needed.

curl --fail-with-body https://app.cmddeck.dev/api/feedback \
  -H 'Content-Type: application/json' \
  --data '{
    "api_key": "fdbk_YOUR_PROJECT_FEEDBACK_KEY",
    "content": "Could we export roast history?",
    "category": "feature",
    "metadata": {
      "source": "percolate",
      "platform": "ios",
      "app_version": "1.0.0",
      "screen": "roast-history"
    }
  }'

Use a real project key before running this example. A successful submission returns HTTP 201:

{
  "success": true,
  "card_id": "UUID_OF_SAVED_FEEDBACK",
  "message": "Feedback submitted successfully. Thank you!",
  "submitted_at": "2026-10-20T00:00:00.000Z"
}

The title is derived from the first 100 characters of the feedback. The full text is stored as its description. Notifications are best effort; a saved submission does not guarantee email delivery.

Request fields

FieldTypeRequiredMeaning
api_keystringYesThe receiving project's fdbk_ key.
contentstringYesNonblank feedback, at most 5,000 characters.
categorystringNoDefaults to other. Default choices: bug, feature, improvement, other. Projects can restrict these.
user_emailstringSometimesOptional valid email address, required if the project enables require_email.
user_namestringNoSubmitter's display name.
metadataobjectNoContext such as platform, app version, screen, or page URL. Use source: "widget" only for widget submissions.

Only collect information users expect to share. Avoid passwords, access tokens, session data, and full URLs containing private query parameters. Names and email addresses supplied here are unverified submitter information, not proof of identity.

PWA and mobile integration

For a product such as Percolate, use the same flow for both clients:

PWA or native feedback form → your backend → cmd-deck Feedback API

On your backend, authenticate the caller if appropriate, validate and limit input, apply your own spam protection, and set the destination key from server configuration. Do not let the client choose the destination key. Derive user details from your authenticated session when you need reliable attribution.

This server-side JavaScript example uses standard fetch:

// Run on your server, after validating the user's feedback.
async function submitFeedback({ content, category = 'other', metadata = {} }) {
  const apiKey = process.env.CMD_DECK_FEEDBACK_KEY;
  if (!apiKey) throw new Error('Feedback integration is not configured');

  const response = await fetch('https://app.cmddeck.dev/api/feedback', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ api_key: apiKey, content, category, metadata })
  });
  const result = await response.json();
  if (!response.ok || !result.success) {
    throw new Error(result.error || 'Feedback could not be submitted');
  }
  return result.card_id;
}

In the form, disable the submit button while sending and only clear the draft after success. Preserve the draft when a request fails. The API has no idempotency key: if a connection drops after saving, blindly retrying can create duplicate feedback. Show the uncertain outcome and let the user choose whether to retry.

Direct browser/native requests with a feedback key are also supported. Browser requests should omit credentials; CORS allows external origins. An embedded key can be extracted and used by others to submit feedback, so use your backend when you need authentication or stronger abuse control. Never embed a full-access cmdk_ key, Supabase service-role key, or database credentials.

Optional website widget

For a website or PWA, an existing script supplies a floating feedback button and form. Native apps should use their own form and the API.

<script src="https://app.cmddeck.dev/api/widget"></script>
<script>
  window.CmdDeckFeedback.init({
    projectId: 'YOUR_PROJECT_UUID',
    apiKey: 'fdbk_YOUR_PROJECT_FEEDBACK_KEY',
    apiUrl: 'https://app.cmddeck.dev/api/feedback'
  });
</script>

Load the script before calling init, and initialize it once. The feedback key is visible in the page. The widget fetches the project's public category/email configuration and supports image attachments.

Attach screenshots

Send multipart/form-data instead of JSON. Use the same field names and repeat the images field for each file. Encode metadata as a JSON string. Let your HTTP client set the multipart boundary; do not set the Content-Type header yourself.

curl --fail-with-body https://app.cmddeck.dev/api/feedback \
  -F 'api_key=fdbk_YOUR_PROJECT_FEEDBACK_KEY' \
  -F 'content=The roast chart overlaps the timer.' \
  -F 'category=bug' \
  -F 'metadata={"source":"percolate","platform":"android"}' \
  -F '[email protected];type=image/png'

Each image must be at most 5 MB and use an allowed raster image type: JPEG, PNG, GIF, or WebP. SVG is rejected. Hosting request-size limits may be lower than the per-image limit; keep screenshots small.

Feedback text can save even when an image is skipped or its upload fails. The response does not confirm individual attachments. Attachment URLs currently use public storage URLs: do not upload sensitive screenshots or assume attachments are private.

Read public project configuration

GET https://app.cmddeck.dev/api/feedback/config/YOUR_PROJECT_UUID

This unauthenticated endpoint returns the project name, allowed categories, require_email, widget_enabled, placeholder text, and success message. It never returns the feedback key or existing submissions. It returns 403 when feedback is disabled and 404 for an unknown project. Configuration may be cached briefly.

An administrator can configure feedback_settings.categories and feedback_settings.require_email in the database. The submission endpoint uses these values; it does not use the historical target_column, auto_tag, or api_enabled settings to route or disable submissions. Disable feedback with feedback_enabled instead.

Errors and limits

HTTP statusMeaningWhat to do
201Feedback saved.Show success and retain card_id if useful.
400Missing/blank/too-long content, disallowed category, or missing/invalid email.Correct the input using the JSON error message.
401Invalid feedback key or feedback disabled.Check activation and the fdbk_ key. A Connect/MCP key will not work.
429Rate limited.Pause submissions; follow Retry-After when present, otherwise the reset information in the response.
500Server failure.Preserve the draft and show a retry option.

The handler currently applies an in-memory allowance of 100 attempts per IP per 24-hour window, including failed attempts. This is per running server instance and can reset on restart; it is not a durable quota or complete spam protection. Middleware may impose an additional, shorter limit. A backend proxy shares its outbound IP allowance among its users. Do not spoof forwarding headers to evade limits.

Rate-limit headers can come from either layer; reset values may be an ISO timestamp or Unix milliseconds. Do not assume a fixed allowance from the documentation. Keep your own submission limits if you need predictable per-user controls.

Feedback listing, conversion, archiving, and restoration require a signed-in cmd-deck workspace member. The public submission key cannot perform those actions.

Verify your integration

  1. Submit a clearly marked test feature request using your receiving project's key.
  2. Open cmd-deck → User Feedback, filter to the project, and confirm the text and category.
  3. Check that a rejected submission preserves the draft and shows an actionable error in your product.
  4. Archive the test item using its Delete action; it remains recoverable under Deleted Items.

See cmd-deck Help for the board and MCP integrations.