Dataset Write API
The Dataset Write API lets you create, update, and delete records of your writable data models from your own scripts or agents, and bulk-update a single attribute across many records at once. It is the write counterpart to the read-only BQL API: reads stay on BQL, and every mutation goes through this surface.
Writes are subject to the same access control and validation as data entered through the Brinqa UI: model, row, and attribute permissions are enforced, and consolidated models route through manual entry. You never send the application; it is derived from the data model. The examples below use Vulnerability records to make the calls concrete, but the same calls work on any writable data model.
Authentication
The Dataset Write 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>
The data model identifier
Every endpoint takes an {identifier} path parameter that names the data model to write to. It is resolved flexibly, in this order:
- A numeric value is resolved as the internal data model id.
- Otherwise, a case-insensitive match against the resource name. The canonical resource name is camelCase plural (for example
vulnerabilities), but case is ignored, soVulnerabilitiesresolves too. - Otherwise, a case-insensitive match against the data model name (for example
Vulnerabilityorvulnerability).
An identifier that matches nothing returns 404. For create, update, and delete, use the specific concrete model that holds the records (for example Vulnerability), never an abstract parent (Finding, Asset, Ticket): abstract parents hold no records of their own and are rejected. Bulk update is the one exception (see Bulk-update an attribute).
Endpoints
| Operation | Method and path |
|---|---|
| Create a record | POST /v1/api/datasets/{identifier} |
| Update a record | PUT /v1/api/datasets/{identifier}/{id} |
| Delete a record | DELETE /v1/api/datasets/{identifier}/{id} |
| Bulk-update an attribute | POST /v1/api/datasets/{identifier}/bulk-update |
The {id} path parameter is a numeric record id (the id, not the hex uid).
The request body for create and update
Create and update take a flat JSON object mapping attribute names to values. There is no envelope.
{
"name": "OpenSSL CVE-2026-0001",
"severity": "High",
"cvssScore": 9.1
}
A few rules apply to every create and update:
- The application is derived, never sent. Do not include
appName,dataModel, orapplication: the API derives the model and its app for you. An app-scoped model with no owning application returns400. - Unknown attributes are rejected, not ignored. Any key that is not a real attribute of the model returns
400, so a typo fails loudly instead of being silently dropped. - Relationships are set by record id. A single-valued relationship is
{"id": <recordId>}; a multi-valued relationship is a list of{"id": <recordId>}. Clear a relationship by sending[]ornullfor that attribute. Resolve a related record's id with a BQL query first; relationships cannot be set by name oruid. The Ticket Creation API uses the same shape for its reference fields. - References to an abstract target need the concrete record id. When an attribute points at an abstract model (for example a relationship to
Asset), pass the concrete record's numericid, not itsuid.
Create a record
Creates one record of the given model. Returns the new record's id in the Location header; the response has no body.
POST /v1/api/datasets/{identifier}
Headers:
Content-Type: application/json
Authorization: ApiKey <your-api-token>
Request body: a flat attribute map (see The request body for create and update).
Response: 201 Created
The Location header points to the new record (/v1/api/datasets/{identifier}/{id}). There is no response body.
Example:
curl -i -X POST 'https://<your-brinqa-instance>/v1/api/datasets/Vulnerability' \
-H 'Content-Type: application/json' \
-H 'Authorization: ApiKey <your-api-token>' \
-d '{
"name": "OpenSSL CVE-2026-0001",
"severity": "High"
}'
Replace <your-brinqa-instance> with the URL of your Brinqa Platform and <your-api-token> with your API token.
Update a record
Updates one record. Each attribute you send replaces the stored value; attributes you omit are left unchanged. For a multi-valued attribute, send the full list you want the record to end with (existing values plus new ones), not just the additions.
PUT /v1/api/datasets/{identifier}/{id}
Headers:
Content-Type: application/json
Authorization: ApiKey <your-api-token>
Request body: a flat attribute map with the attributes to change.
Response: 200 OK with an empty body.
Example:
curl -i -X PUT 'https://<your-brinqa-instance>/v1/api/datasets/Vulnerability/1847261953084' \
-H 'Content-Type: application/json' \
-H 'Authorization: ApiKey <your-api-token>' \
-d '{
"severity": "Critical"
}'
Delete a record
Deletes one record.
DELETE /v1/api/datasets/{identifier}/{id}
Headers:
Authorization: ApiKey <your-api-token>
Response: 204 No Content with an empty body.
A consolidated record can be deleted only when its sole source is manual entry. A record fed by any external integration cannot be deleted through this API, because the next consolidation would simply recreate it; such a delete returns 400.
Example:
curl -i -X DELETE 'https://<your-brinqa-instance>/v1/api/datasets/Vulnerability/1847261953084' \
-H 'Authorization: ApiKey <your-api-token>'
Bulk-update an attribute
Sets a single attribute to one value on every record matched by a BQL query. The query selects the records; the attribute/value pair is what gets written. This launches the platform's autogenerated data-update flow and runs asynchronously.
POST /v1/api/datasets/{identifier}/bulk-update
Here {identifier} is the model you are querying (the main entity of your BQL query). You can update an attribute that the model inherits from a parent (for example description, defined on a parent model): pass the model you are querying and the API resolves the attribute's owning model for you. You do not have to know or name the parent.
Headers:
Content-Type: application/json
Authorization: ApiKey <your-api-token>
Request body:
{
"attribute": "severity",
"value": "Critical",
"query": "FIND Vulnerability WHERE cvssScore >= 9.0"
}
| Field | Type | Required | Description |
|---|---|---|---|
attribute | string | Yes | The single attribute to set on every matched record. The attribute must have Bulk updating enabled in its data model configuration and be of a supported type; an attribute without Bulk updating enabled is rejected with 400. |
value | any | Yes | The value to set. A scalar for a single-valued attribute; a JSON array for a multi-valued attribute; {"id": <recordId>} (or a list of them) for a relationship. Use null or [] to clear. |
strategy | string | No | How to apply value to a multi-valued attribute: REPLACE (default) overwrites the list, ADD appends, REMOVE deletes the given values. Meaningful only for multi-valued attributes. |
query | string | Yes | A BQL query whose main entity is {identifier}. The attribute is written to every record the query matches. |
Response: 202 Accepted
The update runs asynchronously. The Location header points to the launched flow's status (/v1/api/automation/management/{transactionId}); poll it to follow progress and see per-record results. See Flow and Automation Management API for the status shape.
Values are validated before the flow launches: an invalid value, an invalid choice or status option, a non-existent reference, or a strategy other than REPLACE on a single-valued attribute returns 400 and the flow is not launched.
Example:
curl -i -X POST 'https://<your-brinqa-instance>/v1/api/datasets/Vulnerability/bulk-update' \
-H 'Content-Type: application/json' \
-H 'Authorization: ApiKey <your-api-token>' \
-d '{
"attribute": "severity",
"value": "Critical",
"query": "FIND Vulnerability WHERE cvssScore >= 9.0"
}'
Example (add to a multi-valued attribute):
curl -i -X POST 'https://<your-brinqa-instance>/v1/api/datasets/Vulnerability/bulk-update' \
-H 'Content-Type: application/json' \
-H 'Authorization: ApiKey <your-api-token>' \
-d '{
"attribute": "tags",
"value": ["exploited-in-wild"],
"strategy": "ADD",
"query": "FIND Vulnerability WHERE cvssScore >= 9.0"
}'
Value validation
Create, update, and bulk update validate every value against the attribute before writing:
- A value that cannot be converted to the attribute's type (for example an unparseable date or a non-numeric value for a number attribute) returns
400. - A value that is not in the allowed set of a choice or status attribute returns
400. - A relationship to a record that does not exist returns
400.
Required-attribute, maximum-length, and unique-identifier enforcement are not applied by this API.
Permissions and access control
Writes enforce the same model-, row-, and attribute-level access control as the UI, with read-before-write:
- Creating, updating, or deleting a record requires the corresponding create, update, or delete permission on the model, plus access to the specific row and attributes involved.
- A caller without permission receives
403; an unknown model or record receives404.
Rate limits
Each operation is independently rate limited at 60 requests per minute. If you exceed a 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 |
|---|---|---|
200 OK | Update succeeded. | Normal response for update. |
201 Created | Record created. | Normal response for create. The Location header holds the new record URL. |
202 Accepted | Bulk update launched. | Normal response for bulk update. The Location header points to the flow status. |
204 No Content | Record deleted. | Normal response for delete. |
400 Bad Request | The request was rejected before writing. | Unknown attribute (a typo); invalid value for a choice or status attribute; unparseable scalar (date, dateTime, time); reference to a non-existent record; a reference to an abstract model given by uid instead of the concrete record id; the target model is abstract, a cluster, or an application event log; a consolidated model on create or update that does not route to manual entry; delete of a consolidated record whose source is not manual entry only; update or delete of a system-managed record; an app-scoped model with no owning application; a bulk-update attribute that does not have Bulk updating enabled or is of an unsupported type; a strategy other than REPLACE on a single-valued attribute; or a bulk-update query whose main entity is not the path model. |
401 Unauthorized | Authentication failed. | Missing, invalid, expired, or revoked API token. |
403 Forbidden | Not permitted. | The caller lacks model-, row-, or attribute-level permission for the operation. |
404 Not Found | Target not found. | Unknown data model identifier, or unknown record id. |
429 Too Many Requests | Rate limit exceeded. | Too many requests in a short period. Wait for the Retry-After interval and retry. |