openapi: 3.0.0
info:
  title: Nira HTTP API
  description: |
    ### Getting Started
    Before getting started with Nira's HTTP API, be sure you know your organization's Nira URL prefix (e.g. `your-organization.nira.app`). Visit the [login page](https://nira.app/login) if you need any help locating it.

    You will also need an API key. To generate one, [log in](https://nira.app/login) to your Nira organization as an administrator, click the Hamburger menu (Upper left corner), then API Keys → Add. Your API key must be included as the value of the `x-api-key` HTTP header with every request that you make.

    Your API requests should be made to your Nira organization's hostname. For example, if your Nira organization's hostname is `your-organization.nira.app`, the full request URL to retrieve a list of your user groups would be `https://your-organization.nira.app/api/groups`

    ### OpenAPI specification
    The specification for this API is available as [YAML](/openapi/index.yaml) and [JSON](/openapi/index.json) for use with Postman, Swagger UI and client generators. The `x-api-key` header is declared as the `ApiKeyAuth` security scheme.

    ### Can't find the API call that you need?
    Nira's HTTP API is intentionally simple, as our most requested programmability is exposed in the [JavaScript SDK](/js-sdk) Viewer class. Additional API calls can be added upon request -- simply [reach out](https://nira.app/contact) with a brief description of your use case.

    ### Upload automation
    For automation of asset uploads, we typically suggest using our python-based [niraclient uploader](/niraclient). If you'd like to implement upload automation using your own code, please [reach out](https://nira.app/contact) with a brief description of your use case and we will provide our low-level upload API documentation.

    #### Enterprise plan required
    HTTP API access requires a customized Enterprise plan. Please [contact us](https://nira.app/contact) with any questions.

    #### Software vendor integrations
    If you are a software vendor interested in adding Nira upload capabilities into your application, please [contact us](https://nira.app/contact).
tags:
  - name: Callouts
    x-displayName: Callout
    description: The `callout` object represents a Callout in Nira.
  - name: Assets
    x-displayName: Asset
    description: The `asset` object represents an asset in Nira.
servers:
  - url: https://YOUR_ORGANIZATION_GOES_HERE.nira.app
components:
  schemas:
    Callout:
      type: object
      x-tags:
        - Callouts
      title: The Callout object
      description: |
        The unit of the Callout Import/Export feature. This is both the format
        produced by the export endpoint and the format accepted by the import
        endpoint. Exported JSON is an array of these objects.

        What an import does with these objects depends on the import endpoint's
        `mode` parameter.

        In the default `add` mode every object becomes a new callout. Any
        `uuid`, `numericId` or `alphanumericId` in the payload is ignored. Nira
        assigns all of them, and returns the uuid for each new callout in the
        response's `results` array.
        Sending the same request again therefore adds a second copy of every
        callout.

        In `replace` mode every callout on the asset is removed first, including
        photo callouts, and then every object in the request is created as in
        `add` mode. Use it when the request should become the asset's entire
        set of callouts. The `removeCalloutsBeforeImport=1` parameter is
        deprecated in favour of `mode=replace` and cannot be combined with
        `mode`.

        In `update` mode no callouts are created. Each object is matched against
        an existing callout on the asset by the field named in the endpoint's
        `matchField` parameter, either `uuid` or `externalId`, and matched
        callouts are updated in place using only the fields the object actually
        carries, so omitted fields keep their current values. When matching by
        `externalId`, any `uuid` an object carries is ignored. An update cannot change what kind of callout it is, so
        changing `type`, or adding polyline geometry or a photo callout to one
        that has none, is rejected. Remove the callout and import it again
        instead. Objects that match nothing are reported in `results` with a
        `skipped` outcome.

        The import response returns one `results` entry per submitted object, in
        the same order, each carrying an `outcome` and, where a callout was
        affected, its `uuid`.

        A request either applies in full or changes nothing: if it returns an
        error status, nothing was written. A single request accepts at most 60000
        callouts, so larger imports must be split across several requests.

        What each response status means:

        - 2xx: the import applied.
        - 4xx or 556: the request was rejected and will be rejected again if
          resent unchanged. Correct it before sending it again.
        - 401: refresh the credentials once, then retry.
        - 429: wait for the time in the `Retry-After` header, then retry.
        - 500, 502, 503, or 52x: retry as it stands.
        - 504 or 524: retry only when the original request carried an
          `Idempotency-Key`, because the import may already have applied.

        Every error body carries a stable `reason` string on each entry of
        `errors`, such as `import-over-limit`, so clients can branch on that
        instead of on the message text.

        To make a retry safe after a timeout or a lost connection, send an
        `Idempotency-Key` header and repeat the same key on the retry. If the
        first attempt did apply, the retry returns that attempt's original
        response and imports nothing further.

        Every exported callout carries a `uuid`, which is what `update` mode
        matches on. On import every field is optional, apart from the match field
        in `update` mode. A callout with no `type` defaults to `CameraBookmark`.

        In CSV and TSV, callouts are represented as one row per callout with a
        header row of field names, and empty cells are treated as omitted. The
        `content` and `defectType` fields are encoded as JSON
        strings; the display color is split across the `viewColorR`, `viewColorG`,
        and `viewColorB` columns; photo callouts use the `photoCalloutFilename`,
        `photoCalloutTop`, `photoCalloutLeft`, `photoCalloutWidth`, and
        `photoCalloutHeight` columns; and polyline points use zero-indexed
        `point0X`, `point0Y`, `point0Z`, `point1X`, … columns.
      properties:
        uuid:
          type: string
          format: uuid
          description: |
            Nira's unique identifier for the callout. Always present in exported
            callouts.

            When importing callouts:
              - In `add` and `replace` mode, the uuid is optional and any uuid you supply is ignored. Nira generates the new callout's uuid and returns it in the response's `results` array, so you can use it to update that callout later.
              - In `update` mode with `matchField=uuid`, the uuid selects which callout on the asset to update. Every object must carry one, and no two objects may repeat the same one. A malformed uuid rejects the whole request with a `400`. A well-formed uuid that matches nothing on the asset leaves that object alone, which the response reports as a `skipped` outcome, and the rest of the import still applies.
              - In `update` mode with `matchField=externalId`, the uuid is ignored and the `externalId` selects the callout instead.
          example: 9f8e7d6c-5b4a-4392-8171-0e2d3c4b5a69
        numericId:
          type: integer
          readOnly: true
          example: 1
          description: |
            The callout's place in its asset's label sequence, counting from 1.
            Present in exports and ignored on import, where Nira always assigns
            it.

            On import, Nira assigns these in payload order, so the row order in
            your file decides the numbering. In `replace` mode the sequence restarts
            from 1. Otherwise it carries on after the callouts already on the
            asset. A number is never reused, so deleting
            a callout leaves a gap in the sequence. `update` mode never changes
            one.
        alphanumericId:
          type: string
          readOnly: true
          example: A1
          description: |
            The callout's label as users see it in the 3D view, the Layers panel
            and PDF reports. It is `numericId` in a shorter, readable form, so 1
            is A1 and 2 is A2. Present in exports and ignored on import.

            The sequence counts in base 35, running 1 to 9 and then A to Z, so 9
            is A9, 10 is AA, 36 is B1, and 911 is AA1. Sorting a spreadsheet by
            this column only matches the real order for the first 910 callouts on
            an asset, because as text AA1 sorts before AZ. Sort by `numericId`
            instead.
        title:
          type: string
          description: Title for Annotation and Camera Bookmarks. Also see defectTitle parameter.
          example: North pier, west face
        poiDescription:
          type: string
          description: Description text for CameraBookmark callouts. This is the text box shown in the 3D view next to the callout. Whether the box is displayed is controlled by `showDescriptionInView`. Has no effect on other callout types.
          example: Hairline crack running the full height of the pier.
        externalId:
          type: string
          maxLength: 255
          description: |
            Your own identifier for the callout, up to 255 characters, unique
            among the live callouts of an asset. Nira stores and returns it but
            never generates one. Returned in every export, empty when unset.

            When importing callouts:
              - In `add` and `replace` mode, it is stored on the new callout. A value already on another callout of the asset rejects the whole request with a `400`.
              - In `update` mode with `matchField=externalId`, it selects which callout to update. Every object must carry one, and no two objects may repeat the same one.
              - In `update` mode with `matchField=uuid`, it is written to the matched callout. In JSON, `null` clears it. In CSV/TSV an empty cell leaves it unchanged.
          example: inspection-2026-0042
        content:
          type: array
          description: |
            Rich-text body content for Annotation callouts, produced by Nira's
            Slate-based editor. It is an array of block nodes, each of the form
            `{ "type": "p", "children": [ { "text": "..." } ] }`. If provided, it
            must be non-empty. In CSV/TSV it is encoded as a JSON string.
          items:
            type: object
          example:
            - type: p
              children:
                - text: Inspect the corroded flange.
        type:
          type: string
          enum:
            - Annotation
            - Defect
            - Measurement
            - CameraBookmark
          default: CameraBookmark
          description: |
            Specifies the type of callout. Measurement callouts are also referred
            to as Polyline Callouts. These values correspond to the Callout Type
            labels shown in the Nira app as follows: `CameraBookmark` = "Basic
            Text", `Annotation` = "Rich Text Window", `Defect` = "Defect Window",
            `Measurement` = "Measurement / Defect Window". The app then prefixes
            the label according to the callout's geometry, so the same
            `CameraBookmark` reads as "3D Callout - Basic Text" when it is a
            single point and "Polyline - Basic Text" when it has polyline
            geometry.
          example: Defect
        schemaVersion:
          type: integer
          example: 2
          description: |
            The version of the callout data format. Always specify 2. Version 1,
            or omitting this field, is a legacy format and should not be used.
        posX:
          type: number
          format: double
          description: X coordinate for point callouts. When `coordsys` is EPSG:4326, this is the longitude.
          example: -84.268241
        posY:
          type: number
          format: double
          description: Y coordinate for point callouts. When `coordsys` is EPSG:4326, this is the latitude.
          example: 39.344056
        posZ:
          type: number
          format: double
          description: Z coordinate for point callouts. Elevation is always expressed in the coordinate system of the asset's source geometry files (e.g. meters if the source files use meters). It is not converted between vertical datums.
          example: 211.843078
        camPosX:
          type: number
          format: double
          description: Camera position X coordinate.
          example: -84.267912
        camPosY:
          type: number
          format: double
          description: Camera position Y coordinate.
          example: 39.343688
        camPosZ:
          type: number
          format: double
          description: Camera position Z coordinate.
          example: 238.512443
        camTarX:
          type: number
          format: double
          description: Camera target X coordinate.
          example: -84.268241
        camTarY:
          type: number
          format: double
          description: Camera target Y coordinate.
          example: 39.344056
        camTarZ:
          type: number
          format: double
          description: Camera target Z coordinate.
          example: 211.843078
        defectTitle:
          type: string
          description: Title used when the callout's type is 'Defect'.
          example: Spalling at pier base
        defectDescription:
          type: string
          description: Defect description
          example: Concrete spalling with exposed rebar, roughly 40 cm across.
        defectSeverity:
          type: integer
          nullable: true
          description: |
            Numeric value indicating the severity of the defect. One of 1–5, or
            null when unclassified. The values correspond to the labels shown in
            the app: null = Unclassified, 1 = Low, 2 = Normal, 3 = Medium,
            4 = High, 5 = Critical.
          example: 4
        defectType:
          type: array
          items:
            type: string
          example:
            - Corrosion
            - Spalling
          description: |
            The callout's list of tags, shown as "Observation Tags" in the app.
            Despite the field name, tags are supported on all callout types, not
            just Defects. In the app's Layers panel, callouts can be grouped and
            filtered by these tags, and each tag becomes an autocomplete
            suggestion for future tagging across the organization.
        measurementClosed:
          type: boolean
          description: Indicates whether the polyline forms a closed shape. Measurement / polyline callouts only.
          example: false
        measurementLength:
          type: number
          format: double
          readOnly: true
          description: |
            The sum of the lengths of all segments of the polyline. Measurement /
            polyline callouts only. Nira computes this from the callout's points
            and uses it for display, such as measurement labels in the orthophoto
            viewer and the defect dialog. It is ignored on import, and is
            recomputed the next time the asset is opened.
          example: 0.8870510458946228
        measurementArea:
          type: number
          format: double
          readOnly: true
          description: |
            The area enclosed by the polyline. Closed measurement / polyline
            callouts only. Nira computes this from the callout's points as a
            triangulated surface area, so it follows the geometry rather than
            flattening it. It is ignored on import, and is recomputed the next
            time the asset is opened.
          example: 0
        points:
          type: array
          description: |
            An array of ordered 3D points comprising the polyline. Measurement /
            polyline callouts only. When `schemaVersion` is 2 or greater, point
            coordinates follow the declared `coordsys`. For closed polylines the
            export omits the duplicate closing point. In CSV/TSV, points are
            encoded as zero-indexed column triplets `point0X`, `point0Y`,
            `point0Z`, `point1X`, ….

            `update` mode can replace a polyline's points but cannot add geometry
            to a callout that has none, or remove it from one that does. An empty
            array is rejected. Omit the field to leave the geometry as it is.
          items:
            type: array
            items:
              type: number
              format: double
              minItems: 3
              maxItems: 3
          example:
            - - -84.268241
              - 39.344056
              - 211.843078
            - - -84.268236
              - 39.344062
              - 212.239078
        coordsys:
          type: string
          description: |
            The coordinate system used for the callout's 3D positional data
            (`posX/Y/Z`, `camPosX/Y/Z`, `camTarX/Y/Z`, and `points`), in EPSG
            format or 'local' for local coordinates. 'local' can only be used with
            non-georeferenced assets. For latitude/longitude coordinates,
            EPSG:4326 should be specified.

            On export, the value is set from the export request's `coordsys` query
            parameter and is `local`, the asset's EPSG code, or `EPSG:4326`. On
            import, if omitted, the coordinates are assumed to be in the asset's
            internal/local coordinate space and no validation or transformation is
            performed. This is how legacy exports produced before `schemaVersion`
            2 behave.

            Axis order for EPSG:4326: X values (`posX`, `camPosX`, `camTarX`, and
            the first component of each `points` entry) are longitude, and Y
            values are latitude. Z values are elevation in the asset's source-file
            units and are passed through unconverted.
          example: EPSG:4326
        viewColorR:
          type: number
          format: float
          minimum: 0
          maximum: 255
          example: 255
          description: The red component of the callout's display color, in the range 0-255 (not 0.0-1.0).
        viewColorG:
          type: number
          format: float
          minimum: 0
          maximum: 255
          example: 191
          description: The green component of the callout's display color, in the range 0-255.
        viewColorB:
          type: number
          format: float
          minimum: 0
          maximum: 255
          example: 0
          description: The blue component of the callout's display color, in the range 0-255.
        showDescriptionInView:
          type: boolean
          default: true
          description: 'For CameraBookmark callouts, controls whether the callout''s `poiDescription` text box is displayed next to its marker: pinned open in the 2D orthophoto view, and shown when the callout is selected or highlighted in the 3D view. When false, only the marker and title are shown. Corresponds to the "Show Description in View" checkbox in the app. Has no effect on other callout types.'
          example: true
        photocallouts:
          type: array
          description: |
            A list of photo callouts associated with this callout. Currently, only
            one photo callout per callout is supported, and they are only
            applicable to callouts of type Defect. The `filename` of each photo
            callout must refer to an image that has already been uploaded to the
            asset. In CSV/TSV, photo callouts are encoded using the
            `photoCalloutFilename`, `photoCalloutTop`, `photoCalloutLeft`,
            `photoCalloutWidth`, and `photoCalloutHeight` columns.

            `update` mode can change a photo callout but cannot add or remove
            one. An empty array is rejected. Omit the field to leave the photo
            callout as it is.
          items:
            type: object
            required:
              - filename
              - top
              - left
              - width
              - height
            properties:
              filename:
                type: string
                description: The image filename for the photo callout. An image with this name must exist on the asset.
                example: P0002105.jpg
              top:
                type: number
                format: float
                description: The y coordinate of the top-left corner of the photo callout's rectangle, in pixels.
                example: 4026
              left:
                type: number
                format: float
                description: The x coordinate of the top-left corner of photo callout's rectangle, in pixels.
                example: 5076
              width:
                type: number
                format: float
                description: The width of the photo callout's rectangle, in pixels.
                example: 441
              height:
                type: number
                format: float
                description: The height of the photo callout's rectangle, in pixels.
                example: 619
              url:
                type: string
                readOnly: true
                example: https://example.nira.app/a/140d1779/P0002105.jpg?signature=b4f1c2e8a90d4d17&expires=1788366600000
                description: |
                  A signed download URL for the photo callout's image. Present
                  only in exports requested with an API key. It cannot be
                  specified on import and is ignored if present. Imported photo
                  callouts reference their image by `filename` instead.

                  The URL expires between 30 and 60 minutes after the export,
                  and its `expires` query parameter carries the deadline as a
                  millisecond epoch. Treat 30 minutes as the safe figure: the
                  signature is issued on a half-hour boundary so that repeated
                  exports return a cacheable URL, which means the remaining
                  lifetime depends on where in that window the export landed.
                  Re-export rather than reusing URLs from an older response.
        pdfReportScreenshotId:
          type: string
          format: uuid
          description: Unique identifier of the screenshot image shown for this defect callout in PDF reports. Presence of this attribute implies that this callout was configured to be shown in PDF reports. This attribute is only effective when imported into the same Nira organization from which it was exported.
          example: 3f2504e0-4f89-41d3-9a0c-0305e82c3301
    Asset:
      type: object
      title: The Asset object
      properties:
        id:
          type: integer
          description: Unique identifier for the stage (auto-incremented)
          readOnly: true
          example: 4213
        name:
          type: string
          description: Name of the asset
          example: Kings Island Bridge Survey
        uuid:
          type: string
          format: uuid
          description: UUID for the asset
          example: 140d1779-2bb4-4443-bc25-ab8c6f43bc1b
        createdBy:
          type: integer
          description: ID of the user who created the stage
          example: 17
        usesvt:
          type: boolean
          description: Indicates if the stage uses a specific technology (SVT)
          nullable: true
          example: false
        type:
          type: string
          enum:
            - default
            - sculpt
            - photogrammetry
            - volumetric_video
          description: Type of the stage
          default: default
          example: photogrammetry
        passwordRequired:
          type: boolean
          description: Indicates if a password is required to access the stage
          nullable: true
          example: false
        dccname:
          type: string
          description: Name of the DCC software associated with the stage
          nullable: true
          example: RealityScan
        hideBranding:
          type: boolean
          example: false
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
security:
  - ApiKeyAuth: []
paths:
  /api/assets/{suuid}:
    delete:
      summary: Delete an asset
      tags:
        - Assets
      produces:
        - application/json
      parameters:
        - name: x-api-key
          in: header
          required: true
          description: Your API key
          schema:
            type: string
        - in: path
          name: suuid
          description: Assets's base64 encoded uuid
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Returns a deleted record.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                    description: Whether the API call was successful.
                  result:
                    $ref: '#/components/schemas/Asset'
        '403':
          description: Not allowed
          content: {}
        '404':
          description: Not found
          content: {}
  /api/assets/{suuid}/sharing:
    patch:
      summary: Change an asset's visibility
      description: This controls whether an asset is publicly accessible or private.
      tags:
        - Assets
      produces:
        - application/json
      parameters:
        - name: x-api-key
          in: header
          required: true
          description: Your API key
          schema:
            type: string
        - in: path
          name: suuid
          description: Assets's base64 encoded uuid
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                visibility:
                  type: string
                  enum:
                    - private
                    - publicly_accessible
      responses:
        '200':
          description: Returns a patched record.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                    description: Whether the API call was successful.
                  result:
                    $ref: '#/components/schemas/Asset'
        '403':
          description: Not allowed
          content: {}
        '404':
          description: Not found
          content: {}
  /api/assets/{asset_suuid}/callouts/import:
    post:
      tags:
        - Callouts
      summary: Import callouts for a specific asset
      description: |
        Imports callouts onto an asset from a JSON, CSV, or TSV payload. The
        payload format must match the `Content-Type` header: `application/json`,
        `text/csv`, or `text/tab-separated-values`.

        For JSON, the request body is an array of Callout objects, the same
        format the export endpoint produces, so an exported file is accepted
        as-is. See the Callout schema for the full field reference, including
        the CSV/TSV column encodings.

        The `mode` query parameter selects what the import does. In the default
        `add` mode every row becomes a new callout: any `uuid` or
        `numericId` or `alphanumericId` in the payload is ignored. Nira
        assigns all of them.
        In `replace` mode every callout on the asset is removed first,
        including photo callouts, and then every row in the request is created.
        In `update` mode no callouts are created. Each row is matched against
        an existing callout on the asset by the field named in `matchField`
        (`uuid` or `externalId`), and matched callouts are updated in place
        with only the fields the row actually carries. Rows that match nothing
        are skipped and reported as such in the response. On import all fields
        are optional.

        `externalId` is a caller-owned identifier that Nira stores and returns
        but never generates. In CSV/TSV an empty `externalId` cell leaves the
        stored value unchanged, the same as every other omitted column. In JSON
        a `null` clears it.

        A malformed match value rejects the whole request with a 400. A
        well-formed one that matches nothing only skips that row.

        This endpoint requires the organization to be on the Growth or
        Enterprise plan.

        A request either applies in full or changes nothing. If it returns an
        error status, nothing was written, including any callouts
        `mode=replace` had already removed, so a failed
        request is safe to retry once the payload is corrected.

        What each response status means:

        - 2xx: the import applied.
        - 4xx or 556: the request was rejected and will be rejected again if
          resent unchanged. Correct it before sending it again.
        - 401: refresh the credentials once, then retry.
        - 429: wait for the time in the `Retry-After` header, then retry.
        - 500, 502, 503, or 52x: retry as it stands.
        - 504 or 524: retry only when the original request carried an
          `Idempotency-Key`, because the import may already have applied.

        Every error body carries a stable `reason` string on each entry of
        `errors`, such as `import-over-limit`, so clients can branch on that
        instead of on the message text.

        To make a retry safe after a timeout or a lost connection, send an
        `Idempotency-Key` header and repeat the same key on the retry. If the
        first attempt did apply, the retry returns that attempt's original
        response and imports nothing further.

        A single request accepts at most 60000 callouts. Larger imports must be
        split into several requests. Each one is atomic on its own, so send
        them one at a time and check each response before sending the next.

        Body size limits are 40 MB for JSON and 100 MB for CSV/TSV.
      parameters:
        - name: x-api-key
          in: header
          required: true
          description: Your API key
          schema:
            type: string
        - name: Content-Type
          in: header
          required: true
          description: The content type of the request body. Must be one of 'application/json', 'text/csv', or 'text/tab-separated-values'.
          schema:
            type: string
            enum:
              - application/json
              - text/csv
              - text/tab-separated-values
        - name: Idempotency-Key
          in: header
          required: false
          description: |
            An optional identifier you choose for this import, 1 to 200
            characters. Repeating a request with the same key on the same
            asset returns the original response and imports nothing further.
            Using a key that was already used for a different request on the
            same asset returns a 409.
          schema:
            type: string
        - name: asset_suuid
          in: path
          required: true
          description: The 22-character asset short UUID, as it appears in the asset's URL in the Nira app. Numeric asset ids are not accepted.
          schema:
            type: string
        - name: mode
          in: query
          required: false
          description: |
            What the import does:

            - "add" (the default) creates a new callout for every row,
              ignoring any supplied uuid.
            - "replace" removes every callout on the asset, including photo
              callouts, then creates every row in the request.
            - "update" only updates callouts that already exist on the asset,
              and never creates or deletes anything.
          schema:
            type: string
            enum:
              - add
              - replace
              - update
            default: add
        - name: matchField
          in: query
          required: false
          description: |
            The callout field used to match payload rows to existing callouts.
            Required when mode=update, and ignored in the other modes. In update
            mode every row must carry a value for it, and no two rows may repeat
            a value.

            - "uuid" matches by the uuid Nira assigned to each callout.
            - "externalId" matches by the externalId you assigned to each
              callout when it was imported or created. Use it when your own
              system's identifiers are the ones you track. Any uuid a row
              carries is then ignored.
          schema:
            type: string
            enum:
              - uuid
              - externalId
        - name: removeCalloutsBeforeImport
          in: query
          required: false
          description: |
            Deprecated. "1" is equivalent to mode=replace, and is kept for
            callers that already send it. Defaults to "0". Cannot be combined
            with `mode`.
          schema:
            type: string
            enum:
              - '1'
              - '0'
            default: '0'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: array
              items:
                $ref: '#/components/schemas/Callout'
          text/csv:
            schema:
              type: string
              description: CSV text with a header row of Callout field names and one callout per row. See the Callout schema for the CSV/TSV column encodings.
          text/tab-separated-values:
            schema:
              type: string
              description: Tab-separated text with a header row of Callout field names and one callout per row. See the Callout schema for the CSV/TSV column encodings.
      responses:
        '200':
          description: |
            The import ran. Individual rows may still have been skipped or
            failed, so check the counts and `results`.
          content:
            application/json:
              schema:
                type: object
                properties:
                  assetId:
                    type: integer
                    example: 4213
                    description: The asset's internal numeric id.
                  suuid:
                    type: string
                    example: FmITrR_IR1KIsfhXJireEQ
                    description: The asset's short UUID.
                  mode:
                    type: string
                    description: The import mode that was applied. Reported as "replace" when the deprecated removeCalloutsBeforeImport=1 was used.
                    enum:
                      - add
                      - replace
                      - update
                  matchField:
                    type: string
                    nullable: true
                    example: null
                    description: The match field that was applied in update mode, or null in the other modes.
                  removeCalloutsBeforeImport:
                    type: boolean
                    description: Whether existing callouts were removed before the import ran. True in replace mode.
                  removedCount:
                    type: integer
                    example: 0
                    description: Callouts removed in replace mode. Always 0 in the other modes.
                  createdCount:
                    type: integer
                    example: 2
                    description: Callouts created. Always 0 in update mode.
                  updatedCount:
                    type: integer
                    example: 0
                    description: Callouts updated in place. Always 0 in add and replace mode.
                  skippedCount:
                    type: integer
                    example: 0
                    description: Rows whose match value found no callout on the asset. Always 0 in add and replace mode.
                  results:
                    type: array
                    description: |
                      One entry per submitted row, in the same order as the payload, so
                      entry `i` describes submitted row `i`. The submitted callouts are
                      not echoed back.
                    items:
                      type: object
                      properties:
                        outcome:
                          type: string
                          enum:
                            - created
                            - updated
                            - skipped
                        uuid:
                          type: string
                          example: 9f8e7d6c-5b4a-4392-8171-0e2d3c4b5a69
                          description: |
                            The callout's uuid. On a create this is the uuid Nira
                            generated, which is how you map new callouts back to your own
                            records and what you pass as a match value later. On an update
                            it is the uuid of the callout that was matched. Absent on a
                            skip, since no callout was affected.
        '400':
          description: |
            Bad request. Causes include: an unsupported `Content-Type`; more
            than 60000 callouts in one request; an unknown `mode` or
            `matchField`; `mode=update` without a `matchField`; `mode`
            combined with the deprecated `removeCalloutsBeforeImport=1`; an empty
            `photocallouts` or `points` array in update mode; a row with a
            missing, malformed, or duplicated match value in update mode; an
            `externalId` that is not a string of at most 255 characters without
            control characters; an `externalId` repeated within the request or
            already on another callout of the asset; an
            unrecognized value for an enumerated field; a numeric field, point
            coordinate, or photo callout dimension that is not a finite
            number; a callout `coordsys` that does not match the asset's
            coordinate system; a `photocallouts` entry on a callout whose type
            is not Defect; a row that changes an existing callout's type; a row
            that adds polyline geometry to a callout that has none; or a photo
            callout `filename` that is not a file on this asset.
        '403':
          description: The organization's plan does not permit callout import, or the API key lacks permission to modify this asset.
        '404':
          description: No asset on this organization has the supplied asset short UUID.
        '409':
          description: The `Idempotency-Key` was already used for a different import request on this asset.
        '500':
          description: Internal server error.
  /api/assets/{asset_suuid}/callouts/export:
    get:
      tags:
        - Callouts
      summary: Export callouts for a specific asset
      description: |
        Exports callouts related to the specified asset in the requested
        format.

        What each response status means:

        - 2xx: the export is complete.
        - 4xx or 556: the request was rejected and will be rejected again if
          resent unchanged. Correct it before sending it again.
        - 401: refresh the credentials once, then retry.
        - 429: wait for the time in the `Retry-After` header, then retry.
        - 500, 502, 503, or 52x: retry as it stands.

        Every error body carries a stable `reason` string on each entry of
        `errors`, such as `export-invalid-coordsys`, so clients can branch on that
        instead of on the message text.
      parameters:
        - name: x-api-key
          in: header
          required: true
          description: Your API key
          schema:
            type: string
        - name: asset_suuid
          in: path
          required: true
          description: The unique identifier of the asset.
          schema:
            type: string
        - name: format
          in: query
          required: false
          description: The format of the export. One of 'json', 'csv', or 'tsv'. Defaults to 'json' when omitted.
          schema:
            type: string
            default: json
            enum:
              - json
              - csv
              - tsv
        - name: coordsys
          in: query
          required: false
          description: |
            The coordinate system that will be used for the exported callouts 3D positional data (`posX/Y/Z`, `camPosX/Y/Z`, `camTarX/Y/Z`, and `points`). Must be `asset`, `latlong`, or `local`. It is optional. When omitted it defaults to `latlong` for georeferenced assets and `local` otherwise. Requesting `asset` or `latlong` for a non-georeferenced asset returns a 400.
            - `asset`: 3D Positional data will be represented using the coordinate system of the asset. For example, if your asset uses the EPSG:27700 coordinate system, then all 3D positional data will be represented in EPSG:27700. If the asset is not georeferenced, then 3D Positional data will be represented using the coordinate system of the asset's geometry files (i.e. the .obj files).
            - `latlong`: The X and Y coordinate of all 3D Positional data will be represented using latitude / longitude coordinates, also known as EPSG:4326. Elevation (Z) will be represented using coordinate system of your asset's source files. For example, if the .obj files for the asset use meters to store elevation, then the Z coordinates of the callout positional data will use meters.
            - `local`: 3D Positional data will be represented using the coordinate system of the asset's geometry files (i.e. the .obj files). This should only be used for non-georeferenced assets. It is almost always better to specify `asset` instead of `local`.
          schema:
            type: string
            enum:
              - local
              - asset
              - latlong
        - name: include
          in: query
          description: |
            Selects which categories of callout data to include in the export.
            When omitted, all categories are included. When provided, only the
            listed categories are included, and any category not listed is
            excluded. Values are matched case-insensitively.
            - `pointCallouts` Includes 3D point callouts.
            - `polylineCallouts` Includes 3D polyline / measurement callouts.
            - `photoCallouts` Includes 2D photo callouts.
            - `pdfReportScreenshotIds` Preserves *Include in PDF Report* checkbox options and their associated Screenshot IDs. PDF Report Screenshot IDs are only compatible with your own Nira organization. When importing your callout data into a different Nira organization, the *Include in PDF Report* checkbox will need to be manually checked for any 3D callouts that you wish to include in PDF reports.
          required: false
          schema:
            type: array
            items:
              type: string
              enum:
                - pointCallouts
                - polylineCallouts
                - photoCallouts
                - pdfReportScreenshotIds
            default:
              - pointCallouts
              - polylineCallouts
              - photoCallouts
              - pdfReportScreenshotIds
          style: form
          explode: false
      responses:
        '200':
          description: Successful export of callouts
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Callout'
            text/csv:
              schema:
                type: string
            text/tab-separated-values:
              schema:
                type: string
        '400':
          description: Invalid format specified
        '403':
          description: The organization's plan does not permit callout export, or the API key lacks permission to access this asset.
        '404':
          description: Asset not found
        '500':
          description: Server error
  /api/assets/:
    delete:
      summary: Delete multiple assets
      description: Delete multiple assets that were created prior to a particular point in time. This can be useful for cleaning up older assets.
      tags:
        - Assets
      produces:
        - application/json
      parameters:
        - name: x-api-key
          in: header
          required: true
          description: Your API key
          schema:
            type: string
        - in: query
          name: before
          description: A timestamp (milliseconds since epoch) or a relative date string in the format "Nd", where N is a positive integer and "d" represents days. For example, "30d" would delete assets created before 30 days ago, and the timestamp "1741201439963" would delete all assets created before 2025-03-05 19:03:59 UTC.
          required: true
          schema:
            type: string
            pattern: ^\d+d?$
          example: 30d
        - in: query
          name: confirm
          description: If param is set to 'true', the deletion will be executed.
          required: true
          schema:
            type: string
            pattern: ^(true|false)$
          example: 'true'
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                    description: Whether the API call was successful.
                  result:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: number
                          example: 1
                        uuid:
                          type: string
                          example: 550e8400-e29b-41d4-a716-446655440000
                        name:
                          type: string
                          example: Asset
  /api/users/sessions:
    delete:
      summary: Expire all of a user's sessions
      description: Expire all of the login sessions of a user account with the specified email address. Please note, it can take up to 10 minutes after making this request for the user to be forcibly logged out of an ongoing session.
      tags:
        - Users
      produces:
        - application/json
      parameters:
        - name: x-api-key
          in: header
          required: true
          description: Your API key
          schema:
            type: string
        - in: query
          name: email
          required: true
          schema:
            type: string
          example: example@gmail.com
      responses:
        '200':
          description: Returns a successful response.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                    description: Whether the API call was successful.
                  result:
                    type: array
                    description: Array of expired session objects.
                    items:
                      type: object
                      properties:
                        uuid:
                          type: string
                          format: uuid
                          description: Session UUID.
                        email:
                          type: string
                          format: email
                          description: User account's email address.
                        iat:
                          type: integer
                          description: Creation time of the session in seconds since the epoch.
                        exp:
                          type: integer
                          description: Originally scheduled expiration time of the session in seconds since the epoch.
        '403':
          description: Not allowed
          content: {}
        '404':
          description: Not found
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: false
                    description: Whether the API call was successful.
                  errors:
                    type: array
                    items:
                      type: object
