# Feedback

A programmatic feedback channel for Vibecode. AI models, integrations, and external tools submit bugs, suggestions, and questions straight from code — with error context, environment, and reproduction steps. Tickets submitted through the API land in the same tracker as those from the form in your Vibecode account.

**Base URL:** `https://vibecode.bitrix24.com/v1` | **Authorization:** `X-Api-Key`

[Access levels](#access-levels) | [Quick start](#quick-start) | [Full example](#full-example) | [Endpoint reference](#endpoint-reference) | [Error codes](#error-codes)

## Access levels

Permissions depend on whether the key has the `vibe:feedback` scope.

| Key | Create | Read | Update and comments |
|-----|--------|------|---------------------|
| Regular key (`vibe_api_` / `vibe_app_`) | yes | own tickets | withdraw own ticket only |
| Key with the `vibe:feedback` scope | yes | all tickets of its Bitrix24 account | yes, within its Bitrix24 account |

The `vibe:feedback` scope is not granted to a key automatically — enable it explicitly when creating or updating a key. The scope grants extended read and update access to all of the account's tickets, not just its own. Grant it deliberately — for example, to a key that processes the support ticket queue.

## Quick start

Submitting a bug ticket with request context:

```bash
curl -X POST https://vibecode.bitrix24.com/v1/feedback \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "category": "BUG",
    "title": "POST /v1/deals/search returns 500 on empty filter",
    "body": "Calling POST /v1/deals/search with body {\"filter\":{}} gives a 500. Expected an empty array or 400.",
    "context": {
      "endpoint": "/v1/deals/search",
      "request": {"filter": {}},
      "httpStatus": 500,
      "requestId": "req_abc123"
    }
  }'
```

Response:

```json
{
  "success": true,
  "data": {
    "id": "a1b2c3d4-1111-2222-3333-444455556666",
    "category": "BUG",
    "title": "POST /v1/deals/search returns 500 on empty filter",
    "status": "NEW",
    "createdAt": "2026-04-19T10:30:00.000Z"
  }
}
```

The ticket number the user and support see is `VB-` plus the first 8 characters of the `id`. In the example above that is `VB-a1b2c3d4`. Save it so you can reference the ticket in correspondence.

## Full example

Submit a ticket, verify it in the list, and withdraw it if it was created by mistake.

```javascript
const BASE = 'https://vibecode.bitrix24.com/v1'
const headers = { 'X-Api-Key': 'YOUR_API_KEY', 'Content-Type': 'application/json' }

// 1. Submit a ticket
const created = await fetch(`${BASE}/feedback`, {
  method: 'POST',
  headers,
  body: JSON.stringify({
    category: 'DOCS',
    title: 'Typo in the filtering section',
    body: 'The example on /docs/filtering is missing a closing bracket.',
  }),
}).then(r => r.json())

const ticketId = created.data.id
console.log('Ticket created:', 'VB-' + ticketId.slice(0, 8))

// 2. Find it in your list
const list = await fetch(`${BASE}/feedback?category=DOCS&limit=5`, { headers })
  .then(r => r.json())
console.log('Total DOCS tickets:', list.total)

// 3. Withdraw it if created by mistake
await fetch(`${BASE}/feedback/${ticketId}`, {
  method: 'PATCH',
  headers,
  body: JSON.stringify({ status: 'WITHDRAWN' }),
})
```

## Endpoint reference

| Method | Path | Description |
|--------|------|-------------|
| POST | [`/v1/feedback`](/docs/feedback/submit) | Submit a ticket |
| GET | [`/v1/feedback`](/docs/feedback/list) | List tickets with filters and pagination |
| GET | [`/v1/feedback/:id`](/docs/feedback/get) | A single ticket by ID |
| PATCH | [`/v1/feedback/:id`](/docs/feedback/update) | Update status and resolution, withdraw a ticket |
| POST | [`/v1/feedback/:id/comments`](/docs/feedback/comments) | Add a comment to a ticket |
| POST | [`/v1/feedback/attachments`](/docs/feedback/attachments) | Upload an attachment to a ticket or comment |

## Windows / PowerShell and UTF-8

Non-ASCII characters (accents such as ö, ü, é, or any non-Latin script) in `title`, `body`, or `resolution` may turn into question marks (`?`) if the request is sent from Windows PowerShell without explicit UTF-8 serialization. This is not a server-side display problem — the bytes are lost before the HTTP request is sent, on the client side.

**Cause.** By default, PowerShell re-encodes the string from the `-Body` parameter (`Invoke-WebRequest` / `Invoke-RestMethod`) into the system's legacy (non-UTF-8) code page — for example `windows-1252` on Western installs — and any character outside that page is lost before the request is assembled. The `Content-Type: charset=utf-8` header does not help here — by the time it applies, the original bytes are already lost.

**Solution.** Pass the request body as a UTF-8 byte array.

```powershell
# 1. Console output encoding — does not affect how the body is encoded
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8

# 2. Build the JSON and convert it to a UTF-8 byte array
$body = @{
  category = 'DOCS'
  title    = 'Typo in the documentation'
  body     = 'The filtering section example is missing a closing bracket.'
} | ConvertTo-Json -Compress

$bytes = [System.Text.Encoding]::UTF8.GetBytes($body)

# 3. Pass a byte array to -Body (not a string) and set the encoding in Content-Type
Invoke-WebRequest `
  -Uri 'https://vibecode.bitrix24.com/v1/feedback' `
  -Method POST `
  -Headers @{
    'X-Api-Key'    = 'YOUR_API_KEY'
    'Content-Type' = 'application/json; charset=utf-8'
  } `
  -Body $bytes
```

**Common mistakes:**

- Saving the `.ps1` with a UTF-8 BOM — older PowerShell versions may fail to parse the script itself.
- Passing a string to `-Body` (`-Body $body`) instead of a byte array (`-Body $bytes`) — the string is re-encoded through the system code page.
- Relying only on `Content-Type: application/json; charset=utf-8` without `UTF8.GetBytes` — this header does not restore the lost bytes and only declares the stated body encoding to the server.

**Node.js (`fetch`) and Python (`requests`)** encode the body in UTF-8 themselves, no extra steps required. The problem is specific to PowerShell.

## Error codes

| HTTP | Code | Description |
|------|------|-------------|
| 400 | `VALIDATION_ERROR` | `category`, `title`, `body`, or `context` failed validation |
| 400 | `INVALID_FILTER_VALUE` | An unknown `status` or `category` value was passed to the list endpoint |
| 400 | `NO_FILE` | No file was passed to the attachment upload request |
| 403 | `FEEDBACK_SCOPE_REQUIRED` | Update or comment without the `vibe:feedback` scope |
| 404 | `NOT_FOUND` | The ticket does not exist or is not accessible to the key |
| 409 | `FEEDBACK_CLOSED` | An author action on an already closed ticket |
| 409 | `DUPLICATE_FEEDBACK` | A similar ticket was submitted recently |
| 409 | `OPEN_TICKET_EXISTS` | An open ticket on this topic already exists |
| 413 | `IMAGE_TOO_LARGE` | The attachment file exceeds 10 MB |
| 429 | `FEEDBACK_QUOTA_EXCEEDED` | The daily quota for creating tickets is exceeded (per portal or key), with a `Retry-After` header |
| 429 | `RATE_LIMITED` | More than 5 requests per minute to create a ticket or upload an attachment |

For the full list of common API errors, see [Errors](/docs/errors).

## See also

- [Submit a ticket](/docs/feedback/submit)
- [Keys and authorization](/docs/keys-auth)
- [Errors](/docs/errors)
