> ## Documentation Index
> Fetch the complete documentation index at: https://developer-staging.secfix.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Create or update an asset

> Creates a `CUSTOM` or `INFORMATION` asset. It appears in Secfix like an asset added in the app. Set the owner with `ownerId` or `ownerEmail`.

To link the asset to a record in your own system, send `externalSource` and `externalAssetId`. If an asset in the same category already has that pair, the call updates it instead: only the fields you send change, and the response is 200 instead of 201.

Requires the `all:write` scope.



## OpenAPI

````yaml /api-reference/openapi.yaml post /v1/inventory/assets
openapi: 3.0.1
info:
  title: Secfix Public API
  description: >-
    Read and manage your Secfix compliance data from your own systems. Get an
    access token from `POST /oauth/token` with the OAuth 2.0 client credentials
    grant, then send it as a bearer token to the `/v1` endpoints. Errors are
    JSON objects with a stable `code` and the `requestId`. Lists use cursor
    pagination.
  contact:
    name: Secfix Support
    url: https://secfix.com
    email: support@secfix.com
  license:
    name: Proprietary
    url: https://secfix.com/license
  version: 1.0.0
servers:
  - url: https://api.secfix.com
    description: Production
  - url: https://api-staging.secfix.com
    description: Staging
security:
  - bearerAuth: []
tags:
  - name: Employees
    description: >-
      Read the people in your workspace, with their employment status, group and
      onboarding progress.
  - name: Inventory
    description: >-
      Read assets in every inventory category. Create, update and delete
      `CUSTOM` and `INFORMATION` assets.
  - name: OAuth
    description: Get an access token with the OAuth 2.0 client credentials grant.
  - name: Groups
    description: >-
      Read employee groups, their member counts and the onboarding tasks
      assigned to them.
  - name: Health
    description: Check that the API is up.
  - name: Computers
    description: Read the computers in your workspace and their security settings.
paths:
  /v1/inventory/assets:
    post:
      tags:
        - Inventory
      summary: Create or update an asset
      description: >-
        Creates a `CUSTOM` or `INFORMATION` asset. It appears in Secfix like an
        asset added in the app. Set the owner with `ownerId` or `ownerEmail`.


        To link the asset to a record in your own system, send `externalSource`
        and `externalAssetId`. If an asset in the same category already has that
        pair, the call updates it instead: only the fields you send change, and
        the response is 200 instead of 201.


        Requires the `all:write` scope.
      operationId: createAsset
      parameters:
        - name: Idempotency-Key
          in: header
          description: >-
            A unique key, up to 256 characters, that makes the request safe to
            retry. Use a new UUID for each distinct request. If you send the
            same key again, you get the first response back, for at least 24
            hours after the first request completes. Only successful (2xx)
            responses are stored. If the first request is still running, you get
            `409 idempotency_in_progress`: wait the number of seconds in
            `Retry-After`, then retry. See [Idempotency](/idempotency).
          required: true
          schema:
            maxLength: 256
            type: string
            example: b0f8e5a2-3c1d-4e6f-8a9b-0c1d2e3f4a5b
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AssetCreateRequest'
        required: true
      responses:
        '200':
          description: >-
            An asset with the same `externalSource` and `externalAssetId`
            already existed and was updated.
          headers:
            X-RateLimit-Limit:
              description: Requests allowed in the current rate-limit window.
              style: simple
              schema:
                type: integer
            X-RateLimit-Remaining:
              description: Requests remaining in the current rate-limit window.
              style: simple
              schema:
                type: integer
            X-RateLimit-Reset:
              description: Seconds until the rate-limit window resets.
              style: simple
              schema:
                type: integer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AssetDto'
        '201':
          description: Asset created.
          headers:
            X-RateLimit-Limit:
              description: Requests allowed in the current rate-limit window.
              style: simple
              schema:
                type: integer
            X-RateLimit-Remaining:
              description: Requests remaining in the current rate-limit window.
              style: simple
              schema:
                type: integer
            X-RateLimit-Reset:
              description: Seconds until the rate-limit window resets.
              style: simple
              schema:
                type: integer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AssetDto'
        '400':
          description: >-
            The request body is invalid, or the `Idempotency-Key` header is
            missing or longer than 256 characters. Code: `validation_failed` or
            `malformed_request`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
        '401':
          description: >-
            The access token is missing, invalid or replaced by a newer one.
            Code: `unauthorized` or `token_superseded`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
        '403':
          description: >-
            The access token doesn't have the scope this endpoint needs, or the
            category can't be created through the API. Code: `scope_required` or
            `resource_not_writable`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
        '405':
          description: >-
            This path doesn't support the HTTP method. The `Allow` header lists
            the methods it supports. Code: `method_not_allowed`.
          headers:
            Allow:
              description: Comma-separated list of the HTTP methods this path supports.
              style: simple
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
        '409':
          description: >-
            The `Idempotency-Key` was already used with a different body
            (`idempotency_conflict`), the first request with this key is still
            running (`idempotency_in_progress`), or another request created an
            asset with the same `externalSource` and `externalAssetId` at the
            same moment (`write_conflict`). For `idempotency_in_progress` and
            `write_conflict`, retry after `Retry-After`.
          headers:
            Retry-After:
              description: >-
                Seconds to wait before retrying. Sent with
                `idempotency_in_progress` and `write_conflict`.
              style: simple
              schema:
                type: integer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
        '413':
          description: 'The request body is too large. Code: `payload_too_large`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
        '415':
          description: >-
            Send the request body as `application/json`. Code:
            `unsupported_media_type`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
        '429':
          description: >-
            Too many requests. Wait the number of seconds in `Retry-After`, then
            retry. Code: `rate_limited`.
          headers:
            Retry-After:
              description: Seconds to wait before retrying.
              style: simple
              schema:
                type: integer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
        '500':
          description: 'Unexpected server error. Code: `internal_error`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
components:
  schemas:
    AssetCreateRequest:
      required:
        - categoryKey
        - name
      type: object
      properties:
        categoryKey:
          type: string
          description: >-
            Category to create the asset in: `CUSTOM` or `INFORMATION`, in any
            letter case.
          example: CUSTOM
          enum:
            - CUSTOM
            - INFORMATION
        classification:
          type: string
          description: Data classification, in any letter case. Defaults to `INTERNAL`.
          example: CONFIDENTIAL
          enum:
            - RESTRICTED
            - CONFIDENTIAL
            - INTERNAL
            - PUBLIC
            - UNKNOWN
        customIdentifier:
          maxLength: 255
          minLength: 0
          type: string
          description: >-
            Serial number or asset ID, shown in Secfix as **ID/Serial No.** Up
            to 255 characters. Free text and not unique: to identify the asset
            in your system, use `externalSource` and `externalAssetId`.
          example: SN-48213
        dataStored:
          $ref: '#/components/schemas/DataStoredInput'
        description:
          maxLength: 500
          minLength: 0
          type: string
          description: Asset description, up to 500 characters.
          example: Printer in the front office.
        externalAssetId:
          maxLength: 255
          minLength: 0
          type: string
          description: >-
            The asset's id in your system. Up to 255 characters; leading and
            trailing spaces are removed. Must be unique per workspace, category
            and `externalSource` among assets that haven't been deleted. Matched
            exactly. Send it with `externalSource`. You can't change it later.
          example: CI-1042
        externalSource:
          maxLength: 255
          minLength: 0
          type: string
          description: >-
            A name you choose for the system the asset comes from, such as
            `servicenow`. Up to 255 characters; leading and trailing spaces are
            removed. Matched exactly, so send the same value every time. Send it
            with `externalAssetId`.
          example: servicenow-cmdb
        name:
          maxLength: 255
          minLength: 0
          type: string
          description: >-
            Asset name, 1-255 characters. Leading and trailing spaces are
            removed.
          example: Office badge printer
        ownerEmail:
          maxLength: 320
          minLength: 0
          type: string
          description: >-
            Owner's email address, case-insensitive. Send `ownerId` or
            `ownerEmail`, not both.
          example: alex@example.com
        ownerId:
          type: string
          description: >-
            Owner's employee `id`. Send `ownerId` or `ownerEmail`, not both. An
            asset without an owner shows as incomplete.
          format: uuid
          example: a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d
      description: >-
        Creates one `CUSTOM` or `INFORMATION` asset. It appears in Secfix like
        an asset added in the app. Send `externalSource` and `externalAssetId`
        to link it to a record in your system: a later create with the same pair
        in the same category updates this asset instead of adding another, and
        changes only the fields you send.
    AssetDto:
      type: object
      properties:
        accountId:
          type: string
          description: Cloud account id. `CLOUD` assets only.
          example: '123456789012'
        auditStatus:
          type: string
          description: >-
            Audit status, as shown in the Secfix app: `COMPLETE` when every
            field the category needs for an audit is set, otherwise
            `INCOMPLETE`. Left out when Secfix hasn't computed it yet.
            Read-only.
          readOnly: true
          example: INCOMPLETE
          enum:
            - COMPLETE
            - INCOMPLETE
        categoryKey:
          type: string
          description: >-
            The asset's category key. For cloud assets it's the sub-type, such
            as `CLOUD_QUEUE`.
          example: CLOUD
          enum:
            - CLOUD
            - COMPUTER
            - CUSTOM
            - INFORMATION
            - EMPLOYEE
        classification:
          type: string
          description: Data classification.
          example: CONFIDENTIAL
        createdAt:
          type: string
          description: When the asset was created (UTC).
          format: date-time
          example: '2026-01-15T09:30:00.000Z'
        customIdentifier:
          type: string
          description: >-
            Serial number or asset ID, shown in Secfix as **ID/Serial No.**: the
            provider's resource id for cloud assets, the serial number for
            computers, and free text for `CUSTOM` and `INFORMATION` assets. Not
            unique.
          example: i-0abc123def456
        dataStored:
          $ref: '#/components/schemas/DataStoredDto'
        description:
          type: string
          description: Asset description.
          example: Primary production web server.
        email:
          type: string
          description: Employee's email address. Personal data. `EMPLOYEE` assets only.
          example: jane.doe@example.com
        externalAssetId:
          type: string
          description: >-
            The asset's id in your system, as sent on create. Left out for other
            assets.
          example: CI-1042
        externalSource:
          type: string
          description: >-
            The system the asset comes from, as sent on create. Only set on
            `CUSTOM` and `INFORMATION` assets created through the API.
          example: servicenow-cmdb
        id:
          type: string
          description: The asset's id (UUID).
          format: uuid
          example: 3f1c9a2e-8b4d-4e7a-9c1f-2a5b6c7d8e9f
        lastCheckedAt:
          type: string
          description: >-
            When the asset was last checked for compliance (UTC). Left out for
            assets that aren't checked.
          format: date-time
          example: '2026-06-30T02:00:00.000Z'
        name:
          type: string
          description: Asset name.
          example: prod-web-01
        osVersion:
          type: string
          description: Operating system and version. `COMPUTER` assets only.
          example: macOS 14.5
        ownerId:
          type: string
          description: >-
            Owner's employee `id`: the owner the Secfix app shows. The owner can
            be someone `GET /v1/employees` doesn't list, such as a service
            account or a person excluded from compliance, so looking them up
            there can return 404.
          format: uuid
          example: a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d
        ownerSource:
          type: string
          description: Where the owner assignment comes from.
          example: MANUAL
        ownerStatus:
          type: string
          description: >-
            `COMPLETE` when the asset has an owner, otherwise `INCOMPLETE`. Set
            for every category. This isn't the audit status; see `auditStatus`.
            Read-only.
          readOnly: true
          example: COMPLETE
          enum:
            - COMPLETE
            - INCOMPLETE
        region:
          type: string
          description: Cloud region or location. `CLOUD` assets only.
          example: eu-central-1
        serialNumber:
          type: string
          description: Hardware serial number. `COMPUTER` assets only.
          example: C02XY1234ABC
        tags:
          type: array
          description: Tags on the asset.
          items:
            $ref: '#/components/schemas/TagDto'
        type:
          type: string
          description: Asset type within its category.
          example: SERVER
      description: >-
        An inventory asset. Fields marked with a category are only set for
        assets in that category. For `EMPLOYEE` assets, `id` is the employee's
        `id`.
    ProblemDetail:
      type: object
      properties:
        code:
          type: string
          description: >-
            Error code for your error handling. It doesn't change between
            releases.
          example: not_found
        detail:
          type: string
          description: What went wrong in this request.
          example: No asset found for the given id.
        instance:
          type: string
          description: URI that identifies this occurrence of the error.
          example: /v1/inventory/assets/3f1c9a2e-8b4d-4e7a-9c1f-2a5b6c7d8e9f
        requestId:
          type: string
          description: >-
            The request's id, also sent in the `X-Request-Id` header. Include it
            when you contact support.
          example: req_01HZY8Q3M4N5P6R7S8T9V0W1X2
        status:
          type: integer
          description: HTTP status code.
          format: int32
          example: 404
        title:
          type: string
          description: Short summary of the error type.
          example: Not Found
        type:
          type: string
          description: URI that identifies the error type.
          example: https://api.secfix.com/problems/not_found
      description: The JSON body of every error response.
    DataStoredInput:
      type: object
      properties:
        containsPatientData:
          type: boolean
          description: Whether the asset stores patient data.
          example: false
        containsUserData:
          type: boolean
          description: Whether the asset stores user data.
          example: true
        userDataInfo:
          maxLength: 500
          minLength: 0
          type: string
          description: What kind of user data the asset stores, up to 500 characters.
          example: Customer names and email addresses.
      description: What data the asset stores. On create, fields you leave out stay unset.
    DataStoredDto:
      type: object
      properties:
        containsPatientData:
          type: boolean
          description: Whether the asset contains patient data.
          example: false
        containsUserData:
          type: boolean
          description: Whether the asset contains user data.
          example: true
        userDataInfo:
          type: string
          description: What kind of user data the asset contains.
          example: Customer contact records
      description: >-
        What data a `CUSTOM` or `INFORMATION` asset stores. Set it with
        `dataStored` on create or update.
    TagDto:
      type: object
      properties:
        key:
          maxLength: 1000
          minLength: 0
          type: string
          description: >-
            Tag key. On writes, it must be a tag key that exists in your
            workspace.
          example: environment
        readOnly:
          type: boolean
          description: >-
            `true` for tags managed by Secfix, such as `SecfixOwner`. You can't
            change those through the API. Ignored on writes.
          readOnly: true
          example: false
        value:
          maxLength: 255
          minLength: 0
          type: string
          description: Tag value.
          example: production
      description: >-
        A tag on an asset. On writes, `key` must be a tag key in your workspace
        that you can edit.
  securitySchemes:
    bearerAuth:
      type: http
      description: Access token from `POST /oauth/token`.
      scheme: bearer
      bearerFormat: JWT

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.