Ticket Creation API
The Ticket Creation API lets you create a remediation ticket from a selection of findings from your own scripts or agents. It is the programmatic counterpart to creating ad hoc tickets from a findings page in the Brinqa UI: the ticket goes through the same path, so SLA timers, the application event log, and consolidation behave exactly as they do for tickets created through the UI.
You select the findings, name the ticket, and pick an SLA definition; the ticket type is derived from the finding model. A selection of Vulnerability findings produces a vulnerability ticket, a selection of Violation findings produces a violation ticket, and so on. You never send the ticket model. The examples below use Vulnerability findings to make the calls concrete, but the same call works on any concrete finding model.
Authentication
The Ticket Creation API uses API tokens for authentication. See API Token Authentication for instructions on generating, using, and managing API tokens. Pass the token in the Authorization header using the ApiKey scheme:
Authorization: ApiKey <your-api-token>
Create a ticket
Creates one ticket and attaches the selected findings to it.
POST /v1/api/tickets
Headers:
Content-Type: application/json
Authorization: ApiKey <your-api-token>
Request body:
{
"query": "FIND Vulnerability AS v WHERE v.severity = \"Critical\" AND v.status = \"Confirmed active\"",
"name": "Remediate active critical vulnerabilities",
"summary": "Active critical vulnerabilities across the environment",
"description": "Prioritized remediation of every active critical vulnerability.",
"slaPolicy": { "id": 2060003840241509001 }
}
| Field | Type | Required | Description |
|---|---|---|---|
query | string | One of | A BQL query selecting the findings to attach. The finding model is derived from the query's main entity. Mutually exclusive with dataModel + datasetIds. |
dataModel | string | One of | The finding data model of the datasetIds selection: a numeric data model id, a resource name, or a case-insensitive model name (for example Vulnerability). Mutually exclusive with query. |
datasetIds | array of numbers | One of | The ids of the finding records to attach. Requires dataModel. At most 1,000 ids. |
name | string | Yes | Name of the ticket. Maximum 500 characters. |
summary | string | Yes | Short, high-level statement of the ticket's purpose. Maximum 2,000 characters. |
description | string | No | Additional details about the ticket. Maximum 50,000 characters. |
slaPolicy | object | Yes | The SLA definition for the ticket, identified by exactly one of its numeric id (preferred) or its uid: {"id": 2060003840241509001} or {"uid": "3f9c2b7a1d4e8f60"}. Sending both or neither returns 400. Resolve the id with a BQL query over SLADefinition, the same way you resolve other record ids. |
assigned | object | No | Primary remediation owner, as {"id": <recordId>}. Defaults to unassigned. |
delegates | array of objects | No | Alternate remediation owners, as a list of {"id": <recordId>}. |
remediationCampaign | object | No | Remediation campaign to link the ticket to, as {"id": <recordId>}. |
sprint | object | No | Sprint to link the ticket to, as {"id": <recordId>}. |
The finding selection
Every request selects findings in exactly one of two ways:
- By query: a BQL query over one finding model, in the
queryfield. The finding model is the query's main entity; you do not send it separately. - By ids: a finding model in
dataModelplus a non-empty list of record ids indatasetIds.
Sending both or neither returns 400. The selection must match at least one finding, and at most the platform's configured findings limit (1,000 by default); an empty selection or one over the limit returns 400 and no ticket is created.
A minimal body selecting by query:
{
"query": "FIND Vulnerability AS v WHERE v.severity = \"Critical\" AND v.status = \"Confirmed active\"",
"name": "Remediate active critical vulnerabilities",
"summary": "Active critical vulnerabilities across the environment",
"slaPolicy": { "id": 2060003840241509001 }
}
The same ticket selecting by explicit ids. Resolve the ids with a BQL query first; the finding model moves into dataModel because there is no query to derive it from:
{
"dataModel": "Vulnerability",
"datasetIds": [2060003840241508371, 2060003840241508372],
"name": "Remediate active critical vulnerabilities",
"summary": "Active critical vulnerabilities across the environment",
"slaPolicy": { "id": 2060003840241509001 }
}
Select over a specific concrete finding model (for example Vulnerability or Violation), never the abstract Finding parent. Each finding type maps to its own ticket type, so no single ticket type can be derived from Finding; such a request is rejected. This mirrors the UI, where ad hoc ticketing is not available on the All findings page.
The selection only sees findings you can read. Findings outside your access control scope are silently excluded from the selection, exactly as they are hidden from your findings pages in the UI.
Reference fields
The assigned, delegates, remediationCampaign, and sprint fields reference existing records by id, in the same {"id": <recordId>} shape the Dataset Write API uses for relationships. You never send the referenced data model: it is fixed by the corresponding attribute of the derived ticket type (for example, assigned on a vulnerability ticket always points at the model that ticket type's assigned attribute targets).
Resolve the record ids with a BQL query first, for example FIND User WHERE userName = "jdoe@example.com". A referenced record that does not exist, or that you cannot read, returns 404.
Response
201 Created
{
"ticket": {
"id": 2073123085283299328,
"dataModel": "VulnerabilityTicket",
"displayValue": "Remediate active critical vulnerabilities"
},
"findingCount": 42,
"transactionId": "9f3a4c10-2c47-4e11-9d7a-7b1f5b3a82e8"
}
| Field | Type | Description |
|---|---|---|
ticket.id | number | Id of the new ticket record. |
ticket.dataModel | string | The derived ticket data model. |
ticket.displayValue | string | Display value of the ticket (its name). |
findingCount | number | How many findings were selected and attached to the ticket. |
transactionId | string | Identifier of the run that created the ticket. Use it to correlate the run in the application event log. |
The ticket's SLA due dates are computed asynchronously right after creation, so they can lag the response by a moment.
Record ids exceed JavaScript's Number.MAX_SAFE_INTEGER. A naive JSON.parse silently rounds them to a nearby value, and the rounded id no longer resolves. In JavaScript, parse ids from the raw response text as strings, or use a big-integer-aware JSON parser. This applies to every id you send or receive: finding ids, referenced record ids, and the returned ticket id.
Examples
Create a ticket from a BQL query selection:
curl -X POST 'https://<your-brinqa-instance>/v1/api/tickets' \
-H 'Content-Type: application/json' \
-H 'Authorization: ApiKey <your-api-token>' \
-d '{
"query": "FIND Vulnerability AS v WHERE v.severity = \"Critical\" AND v.status = \"Confirmed active\"",
"name": "Remediate active critical vulnerabilities",
"summary": "Active critical vulnerabilities across the environment",
"slaPolicy": { "id": 2060003840241509001 }
}'
Replace <your-brinqa-instance> with the URL of your Brinqa Platform and <your-api-token> with your API token.
Create a ticket from explicit finding ids, with the SLA definition given by its uid:
curl -X POST 'https://<your-brinqa-instance>/v1/api/tickets' \
-H 'Content-Type: application/json' \
-H 'Authorization: ApiKey <your-api-token>' \
-d '{
"dataModel": "Vulnerability",
"datasetIds": [2060003840241508371, 2060003840241508372],
"name": "Remediate CVE-2026-0001 on web servers",
"summary": "OpenSSL CVE-2026-0001 on the web tier",
"slaPolicy": { "uid": "3f9c2b7a1d4e8f60" }
}'
Create a fully specified ticket with an owner, delegates, a remediation campaign, and a sprint:
curl -X POST 'https://<your-brinqa-instance>/v1/api/tickets' \
-H 'Content-Type: application/json' \
-H 'Authorization: ApiKey <your-api-token>' \
-d '{
"query": "FIND Vulnerability AS v WHERE v.severity = \"Critical\" AND v.status = \"Confirmed active\"",
"name": "Remediate active critical vulnerabilities",
"summary": "Active critical vulnerabilities across the environment",
"description": "Prioritized remediation of every active critical vulnerability.",
"slaPolicy": { "id": 2060003840241509001 },
"assigned": { "id": 2060003840241510420 },
"delegates": [{ "id": 2060003840241510421 }, { "id": 2060003840241510422 }],
"remediationCampaign": { "id": 2060003840241511307 },
"sprint": { "id": 2060003840241512118 }
}'
Permissions and access control
Ticket creation enforces the same permissions as the ticket-creation action in the UI:
- The finding selection is scoped to your read access: you can only attach findings you can read.
- Creating the ticket requires permission to execute your tenant's ticket-creation button flow for the finding's model, the same permission that makes the Create ticket action appear on that findings page in the UI. A caller without it, or a finding model with no ticket-creation button flow installed, receives
403. - Referenced records (
assigned,delegates,remediationCampaign,sprint) must be readable by you. A record you cannot read returns the same404as a record that does not exist.
Rate limits
The endpoint is rate limited at 60 requests per minute. If you exceed the limit, the API returns 429 Too Many Requests; wait for the interval given in the Retry-After response header before retrying.
Error handling
| HTTP Status | Meaning | Common cause |
|---|---|---|
201 Created | Ticket created. | Normal response. The body holds the ticket reference, the finding count, and the transaction id. |
400 Bad Request | The request was rejected and no ticket was created. | A missing or over-length name, summary, or description (the message lists every violation); a selection that is both or neither of query and dataModel + datasetIds; invalid BQL; a slaPolicy with both or neither of id and uid; a selection matching no findings; a selection over the configured findings limit (1,000 by default); or a finding model no ticket type can be derived from (for example the abstract Finding model). |
401 Unauthorized | Authentication failed. | Missing, invalid, expired, or revoked API token. |
403 Forbidden | Not permitted. | The caller lacks permission to execute the tenant's ticket-creation button flow on the finding's model, or no such flow exists for that model. |
404 Not Found | Target not found. | Unknown data model identifier, unknown SLA definition, or a referenced record (assigned, delegates, remediationCampaign, sprint) that does not exist or that the caller cannot read. |
409 Conflict | Some selected findings already belong to another remediation. | The body carries a findingIds list with up to 50 of the conflicting finding ids. Exclude them from the selection and retry. |
429 Too Many Requests | Rate limit exceeded. | Too many requests in a short period. Wait for the Retry-After interval and retry. |
409 Conflict body:
{
"status": 409,
"error": "Conflict",
"message": "There are findings that are already included in external remediations; findingIds=[2060003840241508371]",
"findingIds": [2060003840241508371]
}