all:read). Writes require Write access (all:write). See
Scopes.
Categories
Every asset has acategoryKey:
List the categories with
GET /v1/inventory/categories.
What you can write
You can create, update, and delete onlyCUSTOM and INFORMATION assets. Every other category,
including CLOUD, COMPUTER, and EMPLOYEE, is read-only. Their data comes from the Secfix
integrations that discover them, so the API can’t change their owner, description, classification,
or tags either.
- An unknown
categoryKeyreturns400 validation_failed. - In a batch, an asset in a read-only category fails only that record, with
resource_not_writable. - Your workspace’s category settings and the Secfix tags on an asset can stop some fields from changing. See Update an asset and Update assets in a batch.
List and filter assets
GET /v1/inventory/assets returns a cursor-paginated list. Narrow it with query
parameters:
id, call GET /v1/inventory/assets/{id}. An unknown id returns
404 not_found.
Every asset carries two read-only status fields:
ownerStatusisCOMPLETEwhen an owner is assigned, elseINCOMPLETE.auditStatusis the audit status the Secfix app shows:COMPLETEwhen every field the asset’s category requires for an audit is set, elseINCOMPLETE. It’s left out when Secfix hasn’t calculated it yet.
Create an asset
POST /v1/inventory/assets creates one CUSTOM or INFORMATION asset:
201 Created with the full asset, including its new id. If an asset with the same
externalSource and externalAssetId already exists, it is updated instead and the response is
200 OK. See Create or update by your own id.
customIdentifieris the serial number or asset ID shown in Secfix as ID/Serial No. You can edit it later.customIdentifieris not a deduplication key. Two assets can share the samecustomIdentifierorname. To identify an asset in your own system, useexternalSourceandexternalAssetId.- A create body has no
tagsfield. If you sendtags, the API ignores them. Set tags afterwards with the single-assetPATCH. - Without an owner, the asset is still created. It shows as incomplete in Secfix until you assign one.
- The asset looks exactly like one added in the Secfix app.
Set the owner
Set the owner with either field:ownerId: the employee’sidfromGET /v1/employees.ownerEmail: the owner’s email address, matched case-insensitively. It must belong to an active member of your workspace who is included in Secfix.
400 validation_failed on ownerEmail. If no member matches the email, the
request fails with validation_failed (unknown owner). If more than one member matches, it fails
with validation_failed (ambiguous owner). Send ownerId instead.
Without
externalSource and externalAssetId, duplicates are allowed, so a create that runs twice
makes two assets. After a 5xx, nothing may be stored under your Idempotency-Key, so the same key
doesn’t stop a retry from creating the asset again. After a 5xx or a timeout, wait a few minutes.
Then check whether the asset exists before you retry. See Idempotency.Create or update by your own id
SendexternalSource and externalAssetId on a create to tie the asset to its record in your own
system:
externalSourcenames the system the asset comes from. You choose the value. Use the same value every time.externalAssetIdis the asset’s id in that system.
POST /v1/inventory/assets updates it
instead of creating a new one:
- No match: the asset is created, and the response is
201 Created. - A match: that asset is updated, and the response is
200 OKwith the sameid. Only the fields you send change. - A match on an asset that’s hidden in the Secfix app returns
403 resource_not_writable, and the asset isn’t changed. - Send both fields or neither. Sending only one returns
400 validation_failednaming the missing one. - The pair is unique per workspace, category, and
externalSource, among assets that haven’t been deleted. Values are matched exactly, soacme-cmdbandACME-CMDBare different sources. - The same pair in the other category is a different asset.
- The pair is set on create and can’t be changed. A
PATCHthat sendsexternalSourceorexternalAssetIdreturns400 validation_failed. - After you delete the asset, the pair is free again: the next create with it makes a new asset.
- If two requests create the same pair at the same moment, one of them gets
409 write_conflict. Retry it after theRetry-Afterheader: it then updates the asset the other request created.
GET /v1/inventory/assets?externalSource=acme-cmdb&externalAssetId=printer-0042.
Create assets in a batch
POST /v1/inventory/assets/batch-create creates up to 100 assets in one request. Each record in
records has the same shape as a single create:
200 OK with one result per record. For each created record, id is the new asset’s
id. See Read a batch result.
- A batch can contain several records with the same name.
- A record whose
externalSourceandexternalAssetIdmatch an asset in its category updates that asset instead. Its result hasaction: UPDATED, so you can resend a whole list without creating duplicates. - The same
categoryKey,externalSource, andexternalAssetIdtwice in one request rejects the whole request with400 validation_failedonrecords. Nothing is created. - An owner mistake fails only that record, with
validation_failedandfieldset toownerEmailorownerId. Owner mistakes are: sending bothownerIdandownerEmail, an unknown owner, or an ambiguous owner. The batch still returns200.
Update an asset
PATCH /v1/inventory/assets/{id} updates one CUSTOM or INFORMATION asset. Send only the fields you
want to change:
200 OK with the updated asset.
You can update these fields:
name: 1–255 characters. Surrounding spaces are removed, and a blank name is rejected.customIdentifier: up to 255 characters.dataStored:containsUserData,containsPatientData, anduserDataInfo. Fields you leave out stay as they are.ownerIdorownerEmail: the new owner. Send one of them, not both. The owner rules are the same as on create.description: up to 500 characters.classification:RESTRICTED,CONFIDENTIAL,INTERNAL,PUBLIC, orUNKNOWN, in any case.tags: up to 20 entries. Eachkeymust be the name of a tag that already exists in your workspace. Create tags in the Secfix app first: the API can’t create them, and an unknown key returns400 validation_failed. Each tag gets thevalueyou send.tagsreplaces the asset’s tags: a tag you leave out is removed, so send every tag you want to keep.[]removes them all. Leavetagsout to keep the current tags.- Tags that Secfix manages, such as
SecfixOwner, come back on reads withreadOnly: true. You can’t write them: sending one intagsreturns400 validation_failed. Send only keys that reads return withreadOnly: false.
externalSource and externalAssetId are set on create and can’t be changed. Sending either one in a
PATCH returns 400 validation_failed.
- Updating an asset in any other category (a computer, a cloud resource, or an employee) returns
403 resource_not_writable. - Changing the owner,
description, or adataStoredfield returns400 validation_failednaming the field when your category settings don’t include that field, or a Secfix tag on the asset sets it, as in the Secfix app. Sending a field with the value it already has isn’t a change, so it’s never rejected. A rejected request changes nothing.
DELETE requires an Idempotency-Key header.
Update assets in a batch
PATCH /v1/inventory/assets/batch-update updates up to 100 assets in one request. Each record in
records carries an asset id and its own values:
200 OK with one result per record. Each result’s id is the id you sent. See
Read a batch result.
-
Each record accepts the same fields as the single-asset
PATCH, excepttags. -
A record that includes
tagsfails withfieldset totags. Set tags with the single-assetPATCH. -
An asset outside
CUSTOMandINFORMATION(a computer, a cloud resource, or an employee) fails withresource_not_writable. -
Records follow the Secfix app’s bulk edit. Some fields can’t be bulk-edited on an asset: fields
not in your category settings, fields a Secfix tag sets, and fields marked not editable. This can
include
classification. Such a field is skipped, not rejected:- The other fields are saved, and the result is
SUCCESSwith the skipped fields inskippedFields. - When every field you sent is skipped, nothing is saved and the record fails with
not_editable.
PATCHchecks fewer fields: it rejects only an owner,description, ordataStoredchange that your category settings don’t include or a Secfix tag sets, and saves the other fields. - The other fields are saved, and the result is
-
A record with no fields to change fails with
fieldset tobody. -
An owner mistake fails only that record, with
validation_failedandfieldset toownerEmailorownerId. -
An unknown
idfails withnot_found. -
An id can appear only once. A repeated id returns
400 validation_failed.
description and classification, and its category doesn’t
let you bulk-edit description. The second record sent only classification, which its category
doesn’t let you bulk-edit:
Response
Delete an asset
DELETE /v1/inventory/assets/{id} deletes one CUSTOM or INFORMATION asset.
- The response is
204 No Content. DELETEdoesn’t take anIdempotency-Keyheader.- Deleting an asset that was already deleted returns
404 not_found. So does an unknown id. - Deleting an asset in any other category returns
403 resource_not_writable.
Delete assets in a batch
POST /v1/inventory/assets/batch-delete deletes up to 100 assets in one request. Each
record in records carries the id of one asset:
200 OK with one result per record. Each result’s id is the id you sent. See
Read a batch result.
- An unknown id, or one already deleted, fails with
not_found. - An asset outside
CUSTOMandINFORMATIONfails withresource_not_writable. - An id can appear only once. A repeated id returns
400 validation_failed.
Read a batch result
The three batch endpoints (create, update, and delete) share these rules:- A request carries 1–100 records.
- Every batch request requires an
Idempotency-Keyheader. - If the request itself is invalid, the API answers
400and writes nothing. That covers an empty list, more than 100 records, a malformed body, and a repeated id. - An
idinrecordsthat isn’t a UUID fails the whole request with400 malformed_request. - Otherwise the response is always
200 OK, even when some or all records fail. - Each record is saved on its own. A failing record never undoes the others.
Response
Check
status on every result. A 200 doesn’t mean every record succeeded.
Retry failed records
To retry, resend only the failed records under a newIdempotency-Key. Match them by index
to the records you sent.
- Fix
validation_failed,resource_not_writable,not_editable, andnot_foundrecords before you resend them. Anot_editablerecord needs a change in the asset’s category settings in the Secfix app, or a single-assetPATCHafter that. - You can resend
internal_errorrecords as they are. - Wait a second or two before you resend
write_conflictrecords. The record result has noRetry-After. - Reusing the original key replays the stored response, which is kept for at least 24 hours. It doesn’t run the records again.
- The
indexvalues in the new response refer to positions in the new request.
When a batch fails or times out
A batch that answers5xx or times out may have saved some records already, because each record is
saved on its own. Only a 2xx response is stored under your Idempotency-Key. After a 5xx, or a
request that never finished, nothing may be stored, and a resend under the same key can run every record
again, exactly like a new key.
Running a batch update or delete again is safe for your data. Wait a few minutes, then resend it under
the same key. A delete that runs again reports the assets the first run already deleted as
ERROR not_found. An update that runs again overwrites any edits made in Secfix in the meantime.
If every record in a batch create carries externalSource and externalAssetId, resend the whole batch
under a new Idempotency-Key. Records the first request already created are updated, not created
again (action: UPDATED).
Without them, a batch create can create an asset twice if the first request is still running when you
resend it. A request can keep running on our side after your client times out. To avoid duplicates:
- Wait a few minutes, then list your assets in that category and find the
records from your request that already exist. Names and
customIdentifiervalues don’t have to be unique, so match them carefully, or sendexternalSourceandexternalAssetIdnext time. - List again a minute later. If the results changed, the first request is still running: wait and list again until two lists agree.
- Send only the records that don’t exist yet, under a new
Idempotency-Key. Don’t reuse the original key: if the first request finished with200, a changed body under that key returns409 idempotency_conflict. - Check
resultsin the response, as described in Read a batch result.