> ## Documentation Index
> Fetch the complete documentation index at: https://docs.northca.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Upsert active units (bulk)

> Bulk upsert active units.

Body format: `{ "units": [ ... ] }`

Validation:
- Each unit must include a non-empty `id`
- Duplicate ids within the same request are rejected



## OpenAPI

````yaml /openapi.yaml put /mdts/{mdtId}/units/active
openapi: 3.1.0
info:
  title: Northca Partner API
  version: 1.1.0
  description: >-
    Partner-facing REST API for ER:LC and MC communities using Northca MDT.

    ## Response format

    All endpoints return JSON wrapped as:

    **Success**: `{ "ok": true, "data": ... }` **Error**: `{ "ok": false,
    "error": "..." }` 
servers:
  - url: https://api.northca.dev/v1
    description: Production (v1)
security:
  - PartnerKey: []
tags:
  - name: Link Requests
    description: >-
      Request and manage linking a Northca MDT to your ER:LC partner
      integration.
  - name: MDT Links
    description: List linked MDTs and unlink (owner-only) in a guild.
  - name: MDT Info
    description: Read public MDT configuration/info (map, departments, settings).
  - name: Calls
    description: Create, list, and update dispatch calls (CAD).
  - name: Scene Reports
    description: Create and list scene reports (incident summaries).
  - name: Shifts
    description: Create and list shifts (duty sessions).
  - name: Tickets
    description: Create and list unit tickets (tasks/citations/notes).
  - name: Active Units
    description: Upsert, list, delete active units, and trigger panic events (10-33).
paths:
  /mdts/{mdtId}/units/active:
    put:
      tags:
        - Active Units
      summary: Upsert active units (bulk)
      description: |-
        Bulk upsert active units.

        Body format: `{ "units": [ ... ] }`

        Validation:
        - Each unit must include a non-empty `id`
        - Duplicate ids within the same request are rejected
      parameters:
        - $ref: '#/components/parameters/MdtId'
        - $ref: '#/components/parameters/XDiscordGuild'
        - $ref: '#/components/parameters/XDiscordActor'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpsertActiveUnitsBody'
      responses:
        '200':
          description: Upserted units.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/OkEnvelope'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/ActiveUnit'
        '400':
          description: Invalid body or duplicate IDs.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
              examples:
                expectedUnits:
                  value:
                    ok: false
                    error: 'Expected { units: [...] }'
                missingId:
                  value:
                    ok: false
                    error: Each unit must include a non-empty id
                duplicateId:
                  value:
                    ok: false
                    error: Duplicate unit id in request body
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/ServerError'
components:
  parameters:
    MdtId:
      name: mdtId
      in: path
      required: true
      schema:
        type: string
      description: MDT ID.
      example: 0c7f6a0c-2c4f-4f44-b16a-2b6b8d8fe2c2
    XDiscordGuild:
      name: x-discord-guild
      in: header
      required: true
      schema:
        type: string
      description: Discord Guild ID (server/guild context for the request).
      example: '123456789012345678'
    XDiscordActor:
      name: x-discord-actor
      in: header
      required: false
      schema:
        type: string
      description: Discord User ID for the acting user (optional; used for audit fields).
      example: '222222222222222222'
  schemas:
    UpsertActiveUnitsBody:
      type: object
      required:
        - units
      additionalProperties: false
      properties:
        units:
          type: array
          description: |
            Bulk upsert. Each unit must include a non-empty `id`.
            Duplicate ids within the same request are rejected.
          items:
            type: object
            required:
              - id
            additionalProperties: false
            properties:
              id:
                type: string
              officer_name:
                type: string
                nullable: true
              officer_badge:
                type: string
                nullable: true
              department:
                type: string
                nullable: true
              rank:
                type: string
                nullable: true
              status:
                type: string
                nullable: true
      example:
        units:
          - id: ADAM-12
            officer_name: B. Bubbleton
            officer_badge: '512'
            department: Patrol
            rank: PO
            status: 10-8
          - id: LINCOLN-3
            officer_name: A. Smith
            officer_badge: '144'
            department: Traffic
            rank: SGT
            status: 10-7
    OkEnvelope:
      type: object
      required:
        - ok
        - data
      properties:
        ok:
          type: boolean
          const: true
        data:
          description: Response payload.
          nullable: false
          type: object
          additionalProperties: true
    ActiveUnit:
      type: object
      description: Active unit record.
      additionalProperties: true
      properties:
        id:
          type: string
        mdt_code:
          type: string
          nullable: true
        officer_name:
          type: string
          nullable: true
          description: >
            Note: the API may store empty strings ("") for unset values on
            upsert.
        officer_badge:
          type: string
          nullable: true
          description: >
            Note: the API may store empty strings ("") for unset values on
            upsert.
        department:
          type: string
          nullable: true
          description: >
            Note: the API may store empty strings ("") for unset values on
            upsert.
        rank:
          type: string
          nullable: true
          description: >
            Note: the API may store empty strings ("") for unset values on
            upsert.
        status:
          type: string
          nullable: true
          description: >
            Note: the API may store empty strings ("") for unset values on
            upsert.
        last_seen:
          type: string
          format: date-time
          nullable: true
        created_by_discord_id:
          type: string
          nullable: true
        updated_at:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time
          nullable: true
    ErrorEnvelope:
      type: object
      required:
        - ok
        - error
      properties:
        ok:
          type: boolean
          const: false
        error:
          type: string
      additionalProperties: true
      example:
        ok: false
        error: Unauthorized
  responses:
    Unauthorized:
      description: Missing/invalid authentication or required headers.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            ok: false
            error: Unauthorized
    Forbidden:
      description: You do not have access to this resource in the provided guild context.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            ok: false
            error: MDT not linked to this guild
    ServerError:
      description: Internal server error.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
  securitySchemes:
    PartnerKey:
      type: apiKey
      in: header
      name: x-partner-key
      description: Partner API key provided by Northca.

````