ArxDeck
Integrations

Feedback ingest API

Submit bug reports and feature requests from external apps into project feedback.

Feedback ingest API

The feedback ingest API lets external applications submit bug reports and feature requests into a project's feedback inbox. Project admins triage items in the dashboard and can convert them to tickets.

Enablement

Project admins open Settings → Feedback:

  • Toggle Feedback enabled (gates sidebar entry and API access)
  • Create and revoke ingest API tokens (plaintext shown once)
  • Toggle Allow in-app submission for dashboard forms (the external ingest API still works when in-app submission is off)

In-app project submission and the platform self-report flow use the same service layer — browsers do not hold ingest tokens for those paths.

Agents on ArxDeck MCP must call get_project_info and confirm capabilities.feedback before assuming ingest is enabled.

Overview

The Feedback ingest API lets external applications submit bug reports and feature requests into a project. All endpoints require server-side calls — never expose your ingest token in client-side code or mobile apps.

Authentication

Send a project ingest token in the Authorization header:

Authorization: Bearer arx_fb_YOUR_TOKEN

Create tokens in Settings → Feedback. Tokens use the arx_fb_ prefix and are shown in plaintext only once at creation.

Base URL

{your dashboard origin}/api/feedback/v1

Replace {your dashboard origin} with the HTTPS origin of your ArxDeck dashboard (for example https://workspace.example.com).

Typical submit flow

  1. Presign — POST /uploads/presign to get a presigned PUT URL and storageKey for each image.
  2. Upload — PUT image bytes directly to the presigned uploadUrl with the matching Content-Type header (not an ingest API route).
  3. Submit — POST /submissions with the feedback body and imageKeys from step 1.

External user identity

Every request identifies the submitter with an externalUserId string (your application's user ID). List, detail, comment, and close endpoints only return or modify items owned by that ID.

Error responses

Failed requests return JSON with an error message. Service errors may include a code field; rate limits include retryAfterSeconds.

{
  "error": "Not found",
  "code": "not_found"
}
FieldTypeRequiredDescription
errorstringYesHuman-readable error message.
codestringNoMachine-readable code (for example not_found, forbidden, invalid_image_keys, rate limit reasons).
retryAfterSecondsnumberNoPresent on 429 responses — seconds to wait before retrying.

Common status codes: 401 missing or invalid token; 403 revoked token, feedback disabled, or forbidden; 404 not found; 422 invalid status transition; 429 rate limited; 503 image storage not configured for this deployment.

Rate limits

ScopeLimit
Project100 requests per minute across all ingest routes.
Submissions10 accepted submissions per externalUserId per hour.
Comments60 accepted comments per externalUserId per hour.

Endpoints

POST /uploads/presign

Request a presigned PUT URL for uploading one image. The returned storageKey must be included in a submission's imageKeys after the upload completes.

Request body

FieldTypeRequiredDescription
externalUserIdstringYesYour application's user identifier.
filenamestringYesOriginal filename (1–255 characters).
contentTypestringYesOne of image/png, image/jpeg, image/webp.

Response — 201 Created

FieldTypeDescription
idstringPending upload record ID.
storageKeystringKey to pass in submission imageKeys after upload.
uploadUrlstringPresigned URL — PUT image bytes here.
expiresAtstringISO 8601 expiry for the presigned URL (15 minutes).

Example

curl -X POST "{your dashboard origin}/api/feedback/v1/uploads/presign" \
  -H "Authorization: Bearer arx_fb_YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "externalUserId": "user-123",
    "filename": "screenshot.png",
    "contentType": "image/png"
  }'
{
  "id": "clx9up0000000000000000001",
  "storageKey": "feedback/PROJECT_ID/a1b2c3d4-e5f6-7890-abcd-ef1234567890/screenshot.png",
  "uploadUrl": "https://storage.example.com/feedback/...?X-Amz-Signature=...",
  "expiresAt": "2026-07-06T01:45:00.000Z"
}

Then upload the image (not an ingest API route):

curl -X PUT "<uploadUrl from response>" \
  -H "Content-Type: image/png" \
  --data-binary @screenshot.png

POST /submissions

Create a new feedback item. Upload images via presigned URLs first and pass their storageKeys.

Request body

FieldTypeRequiredDescription
externalUserIdstringYesYour application's user identifier.
titlestringYesShort summary (1–500 characters).
typestringNobug or feature_request. Defaults to bug.
bodystringNoDetailed description (max 50,000 characters).
displayNamestringNoSubmitter display name (max 200 characters).
emailstringNoSubmitter email (valid email or empty string).
imageKeysstring[]NoStorage keys from presign (max 5). Each must be uploaded, unconsumed, and not expired.
contextUrlstringNoPage URL where feedback was submitted (max 2,000 characters).
contextUserAgentstringNoClient user agent (max 2,000 characters).
contextAppVersionstringNoApp version string (max 100 characters).

Response — 201 Created

FieldTypeDescription
idstringFeedback item ID.
typestringbug or feature_request.
titlestringItem title.
statusstringAlways new on create.
createdAtstringISO 8601 creation timestamp.

Example

curl -X POST "{your dashboard origin}/api/feedback/v1/submissions" \
  -H "Authorization: Bearer arx_fb_YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "externalUserId": "user-123",
    "title": "Button does not work",
    "type": "bug",
    "body": "Steps to reproduce:\n1. Open settings\n2. Click Save",
    "displayName": "Jane",
    "email": "jane@example.com",
    "imageKeys": ["feedback/PROJECT_ID/a1b2c3d4-e5f6-7890-abcd-ef1234567890/screenshot.png"],
    "contextUrl": "https://myapp.com/settings",
    "contextUserAgent": "MyApp/1.2.0",
    "contextAppVersion": "1.2.0"
  }'
{
  "id": "clx9sub00000000000000001",
  "type": "bug",
  "title": "Button does not work",
  "status": "new",
  "createdAt": "2026-07-06T01:30:00.000Z"
}

GET /submissions?externalUserId=

List all feedback items submitted by an external user in this project.

Query parameters

FieldTypeRequiredDescription
externalUserIdstringYesYour application's user identifier.

Response — 200 OK

FieldTypeDescription
itemsobject[]Array of summary objects, newest first.
items[].idstringFeedback item ID.
items[].typestringbug or feature_request.
items[].titlestringItem title.
items[].statusstringnew, acknowledged, planned, in_progress, resolved, closed, wont_fix, or closed_by_submitter.
items[].createdAtstringISO 8601 creation timestamp.
items[].updatedAtstringISO 8601 last update timestamp.

Example

curl "{your dashboard origin}/api/feedback/v1/submissions?externalUserId=user-123" \
  -H "Authorization: Bearer arx_fb_YOUR_TOKEN"
{
  "items": [
    {
      "id": "clx9sub00000000000000001",
      "type": "bug",
      "title": "Button does not work",
      "status": "acknowledged",
      "createdAt": "2026-07-06T01:30:00.000Z",
      "updatedAt": "2026-07-06T02:00:00.000Z"
    }
  ]
}

GET /submissions/:id?externalUserId=

Get full detail for one feedback item, including images (with presigned view URLs), comments, and linked ticket if converted. Only returns items owned by the given externalUserId.

Path parameters

FieldTypeRequiredDescription
idstringYesFeedback item ID.

Query parameters

FieldTypeRequiredDescription
externalUserIdstringYesMust match the item owner.

Response — 200 OK

FieldTypeDescription
itemobjectFull feedback item.
item.idstringFeedback item ID.
item.typestringbug or feature_request.
item.titlestringItem title.
item.bodystringDetailed description.
item.statusstringCurrent workflow status.
item.externalUserIdstringOwner's external user ID.
item.displayNamestring | nullSubmitter display name.
item.emailstring | nullSubmitter email.
item.contextUrlstring | nullSubmitted-from URL.
item.contextUserAgentstring | nullClient user agent.
item.contextAppVersionstring | nullApp version.
item.convertedTicketIdstring | nullLinked ticket ID if converted.
item.createdAtstringISO 8601 creation timestamp.
item.updatedAtstringISO 8601 last update timestamp.
item.imagesobject[]Attached images with presigned view URLs.
item.images[].urlstringPresigned GET URL (5-minute TTL).
item.commentsobject[]Comments in chronological order.
item.comments[].authorTypestringsubmitter or triage.
item.convertedTicketobject | null{ id, title } when converted to a ticket.

Example

curl "{your dashboard origin}/api/feedback/v1/submissions/clx9sub00000000000000001?externalUserId=user-123" \
  -H "Authorization: Bearer arx_fb_YOUR_TOKEN"

POST /submissions/:id/comments

Add a comment to a feedback item as the submitter. Only the item owner can comment via the ingest API.

Path parameters

FieldTypeRequiredDescription
idstringYesFeedback item ID.

Request body

FieldTypeRequiredDescription
externalUserIdstringYesMust match the item owner.
bodystringYesComment text (1–10,000 characters).

Response — 201 Created

FieldTypeDescription
idstringComment ID.
bodystringComment text.
createdAtstringISO 8601 creation timestamp.

Example

curl -X POST "{your dashboard origin}/api/feedback/v1/submissions/clx9sub00000000000000001/comments" \
  -H "Authorization: Bearer arx_fb_YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "externalUserId": "user-123",
    "body": "I can reproduce this on iOS 18 as well."
  }'

PATCH /submissions/:id/status

Close a feedback item as the submitter. Only allowed when the current status is new or acknowledged. Sets status to closed_by_submitter.

Path parameters

FieldTypeRequiredDescription
idstringYesFeedback item ID.

Request body

FieldTypeRequiredDescription
externalUserIdstringYesMust match the item owner.
statusstringYesMust be closed_by_submitter.

Response — 200 OK

FieldTypeDescription
idstringFeedback item ID.
statusstringUpdated status (closed_by_submitter).

Example

curl -X PATCH "{your dashboard origin}/api/feedback/v1/submissions/clx9sub00000000000000001/status" \
  -H "Authorization: Bearer arx_fb_YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "externalUserId": "user-123",
    "status": "closed_by_submitter"
  }'

Do not expose ingest tokens in client-side JavaScript or public repositories. Rotate tokens if leaked.