## Submit a ticket

`POST /v1/feedback`

Creates a ticket in the feedback tracker. Accepts any valid key — the Bitrix24 account and author are determined from the key owner. The body is passed flat, without a `fields` wrapper.

## Request fields (body)

| Field | Type | Required | Description |
|-------|------|:--------:|-------------|
| `category` | string | yes | One of: `BUG`, `SUGGESTION`, `DOCS`, `CHAT`, `BOTS`, `OTHER`. Case-insensitive |
| `title` | string | yes | Title, 3–200 characters |
| `body` | string | yes | Description, 10–20000 characters. The lower bound is lifted when attachments are provided via `attachmentIds` |
| `context` | object | no | Arbitrary JSON up to 10 KB — endpoint, request body, `requestId`, tool version |
| `attachmentIds` | array | no | Up to 5 IDs of pre-uploaded attachments. How to upload — [Upload an attachment](./attachments.md) |

### Categories

| Category | When to choose |
|----------|----------------|
| `BUG` | Behavior diverges from the documentation or breaks |
| `SUGGESTION` | A proposal for an improvement or a new capability |
| `DOCS` | Documentation is incomplete, outdated, or diverges from API behavior |
| `CHAT` | Feedback about the chat platform — messages, dialogs, files |
| `BOTS` | Feedback about the bot platform — registration, events, commands |
| `OTHER` | Does not fit the other categories |

## Examples

### curl — personal key

```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",
      "httpStatus": 500,
      "requestId": "req_abc123"
    }
  }'
```

### curl — OAuth application

```bash
curl -X POST https://vibecode.bitrix24.com/v1/feedback \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -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",
      "httpStatus": 500,
      "requestId": "req_abc123"
    }
  }'
```

### JavaScript — personal key

```javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/feedback', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    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', httpStatus: 500, requestId: 'req_abc123' },
  }),
})

const { data } = await res.json()
console.log('Ticket number:', 'VB-' + data.id.slice(0, 8))
```

### JavaScript — OAuth application

```javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/feedback', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    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', httpStatus: 500, requestId: 'req_abc123' },
  }),
})

const { data } = await res.json()
```

## Response fields

| Field | Type | Description |
|-------|------|-------------|
| `success` | boolean | Always `true` on success |
| `data.id` | string | Ticket UUID. The human-readable number is `VB-` plus the first 8 characters |
| `data.category` | string | Ticket category |
| `data.title` | string | Title |
| `data.status` | string | Initial status — always `NEW` |
| `data.createdAt` | string | Creation date in ISO 8601 format |

## Response example

```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"
  }
}
```

## Error response example

400 — validation failed:

```json
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "title must be 3-200 characters"
  }
}
```

## Errors

| HTTP | Code | Description |
|------|------|-------------|
| 400 | `VALIDATION_ERROR` | `category`, `title`, `body`, or `context` failed validation |
| 409 | `DUPLICATE_FEEDBACK` | A similar ticket was submitted recently. `error.existingTicketId` points to the existing ticket |
| 409 | `OPEN_TICKET_EXISTS` | An open ticket on this topic already exists. `error.existingTicketId` points to it |
| 429 | `FEEDBACK_QUOTA_EXCEEDED` | The daily quota for creating tickets is exceeded (per Bitrix24 account or per key). Returns a `Retry-After` header and the `scope`, `limit`, `retryAfter` fields |
| 401 | `MISSING_API_KEY` | The `X-Api-Key` header was not passed |
| 429 | `RATE_LIMITED` | More than 5 requests per minute from one key |

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

## Known specifics

**The source is recorded automatically.** Tickets via the API have `source` equal to `api`, those submitted from the form in your Vibecode account — `ui`. The Bitrix24 account and author are taken from the key owner, no need to enter them manually.

**Context speeds up triage.** Put `requestId`, versions, the request body, and the server response into `context` — up to 10 KB of arbitrary JSON. With full context a ticket is triaged without follow-up questions.

**Duplicate protection and quota.** A request with similar content is rejected as `409 DUPLICATE_FEEDBACK`, and an open ticket with the same title as `409 OPEN_TICKET_EXISTS`. In both cases `error.existingTicketId` points to the existing ticket if the key has access to it. Separately, a daily quota applies per Bitrix24 account and per key — on exceeding it, `429 FEEDBACK_QUOTA_EXCEEDED` arrives with a `Retry-After` header. This is not the same as the 5-requests-per-minute limit (`429 RATE_LIMITED`).

## See also

- [List tickets](/docs/feedback/list)
- [Upload an attachment](/docs/feedback/attachments)
- [Feedback](/docs/feedback)
- [Errors](/docs/errors)
