> ## 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 assets in a batch

> Creates up to 100 `CUSTOM` or `INFORMATION` assets in one request. Each record is validated and saved on its own, so one invalid record doesn't stop the others. The response is 200 with one result per record, in request order.

A record whose `externalSource` and `externalAssetId` match an existing asset in its category updates that asset instead: its `action` is `UPDATED`, and only the fields you send change. So you can resend the same list without creating duplicates. If the same pair appears twice in one request, the whole request is rejected.

A batch counts as one write request for rate limiting. Requires the `all:write` scope.



## OpenAPI

````yaml /api-reference/openapi.yaml post /v1/inventory/assets/batch-create
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/batch-create:
    post:
      tags:
        - Inventory
      summary: Create assets in a batch
      description: >-
        Creates up to 100 `CUSTOM` or `INFORMATION` assets in one request. Each
        record is validated and saved on its own, so one invalid record doesn't
        stop the others. The response is 200 with one result per record, in
        request order.


        A record whose `externalSource` and `externalAssetId` match an existing
        asset in its category updates that asset instead: its `action` is
        `UPDATED`, and only the fields you send change. So you can resend the
        same list without creating duplicates. If the same pair appears twice in
        one request, the whole request is rejected.


        A batch counts as one write request for rate limiting. Requires the
        `all:write` scope.
      operationId: createAssetsBatch
      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/BatchAssetCreateRequest'
        required: true
      responses:
        '200':
          description: >-
            Every record was processed. Check `status` on each entry in
            `results`; each entry has the record's `index`. Resend only the
            failed records, with a new `Idempotency-Key`.
          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/BatchResponse'
        '400':
          description: >-
            The request is invalid: `records` is missing, empty or has more than
            100 entries, the same `categoryKey`, `externalSource` and
            `externalAssetId` appear twice, 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. Code:
            `scope_required`.
          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`), or the first request with this key is
            still running (`idempotency_in_progress`). For
            `idempotency_in_progress`, retry after `Retry-After`.
          headers:
            Retry-After:
              description: >-
                Seconds to wait before retrying. Sent with
                `idempotency_in_progress`. A `write_conflict` on one record is
                reported in that record's result, not as a 409.
              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:
    BatchAssetCreateRequest:
      required:
        - records
      type: object
      properties:
        records:
          maxItems: 100
          minItems: 1
          type: array
          description: >-
            The assets to create, 1-100. Each has the same fields as a single
            create.
          items:
            $ref: '#/components/schemas/AssetCreateRequest'
      description: >-
        Creates up to 100 `CUSTOM` or `INFORMATION` assets. Each record is
        validated on its own.
    BatchResponse:
      type: object
      properties:
        results:
          type: array
          description: One result per record, in request order.
          items:
            $ref: '#/components/schemas/BatchItemResult'
      description: >-
        Results of a batch request, one per record. The response is 200 even
        when some or all records failed. Resend only the failed records, with a
        new `Idempotency-Key`.
    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.
    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.
    BatchItemResult:
      type: object
      properties:
        action:
          type: string
          description: 'For a successful create: whether the asset was created or updated.'
          enum:
            - CREATED
            - UPDATED
        code:
          type: string
          description: Why the record failed. Only set when it failed.
          example: validation_failed
          enum:
            - validation_failed
            - resource_not_writable
            - not_editable
            - not_found
            - write_conflict
            - internal_error
        field:
          type: string
          description: >-
            The request field that failed validation. Only set for
            `validation_failed`.
          example: ownerEmail
        id:
          type: string
          description: >-
            For a create, the `id` of the new or matched asset; left out when
            the create failed. For a delete or update, the `id` you sent.
          format: uuid
        index:
          type: integer
          description: Position of the record in `records`, starting at 0.
          format: int32
          example: 0
        message:
          type: string
          description: Short explanation of the failure. Only set when the record failed.
          example: unknown owner
        skippedFields:
          type: array
          description: >-
            Batch updates only. Fields you sent that weren't changed, because
            the asset's category doesn't allow bulk editing them: the field
            isn't configured, a Secfix tag sets it, or it's marked not editable.
            On a `SUCCESS` record the other fields were saved; on a
            `not_editable` record nothing was. Left out when every change was
            applied.
          items:
            type: string
            example: description
        status:
          type: string
          description: Outcome of the record.
          enum:
            - SUCCESS
            - ERROR
      description: The result of one record in a batch.
    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.
  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.