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

# Bulk Update Device Metadata

> Bulk-update the deviceMetadata (properties) for multiple devices in a single request, identified by their serial/identifier

## Overview

This endpoint allows you to update the metadata (properties) for up to **1,000 devices** in a single request. Devices are identified by their serial/identifier and scoped to your organization via API key.

<Warning>
  This performs a **full replacement** of `deviceMetadata` for each device — it does not merge with existing metadata. To preserve existing properties, the caller must include them in the payload.
</Warning>

* Only existing devices are updated; no devices are created. Unrecognized identifiers are reported as `not_found`.
* All matching devices are updated in a single SQL statement for efficiency.
* `updatedAt` is set to the current time for all updated devices.

## Authentication

<ParamField header="x-api-key" type="string" required>
  Your organization's API key
</ParamField>

## Request Body

<ParamField body="devices" type="Record<string, Record<string, unknown>>" required>
  Object mapping device identifiers (serials) to their new metadata properties. Each value must be a JSON object.
</ParamField>

### Constraints

* `devices` must contain **at least 1** entry
* Maximum **1,000** devices per request
* Each metadata value must be a JSON object (not `null`, array, or primitive)
* Identifier keys are trimmed of leading/trailing whitespace before matching

## Response

### 200 OK — Success

<ResponseField name="success" type="boolean">
  Always `true` on a 200 response.
</ResponseField>

<ResponseField name="updated" type="number">
  Count of devices that were found and updated.
</ResponseField>

<ResponseField name="notFound" type="number">
  Count of identifiers that matched no device in the organization.
</ResponseField>

<ResponseField name="results" type="BulkMetadataResponseItem[]">
  Per-identifier result in the same order as the input keys.
</ResponseField>

#### BulkMetadataResponseItem

| Field        | Type                         | Description                                           |
| ------------ | ---------------------------- | ----------------------------------------------------- |
| `identifier` | `string`                     | The trimmed device identifier                         |
| `deviceId`   | `string`                     | The device's UUID if found, empty string if not found |
| `status`     | `"updated"` \| `"not_found"` | Whether the device was updated or didn't exist        |

### 400 Bad Request

```json theme={null}
{ "error": "<message>" }
```

Possible messages:

| Message                                                                       | Cause                                           |
| ----------------------------------------------------------------------------- | ----------------------------------------------- |
| `"Invalid JSON payload"`                                                      | Body is not valid JSON                          |
| `"devices is required and must be an object mapping identifiers to metadata"` | Missing or wrong type                           |
| `"devices must contain at least one entry"`                                   | Empty object                                    |
| `"Too many devices. Maximum 1000 per request"`                                | Exceeds limit                                   |
| `"Invalid metadata for device \"<identifier>\": must be a JSON object"`       | A metadata value is `null`, array, or primitive |

### 401 Unauthorized

```json theme={null}
{ "error": "Missing API key" }
```

```json theme={null}
{ "error": "Invalid API key" }
```

### 500 Internal Server Error

```json theme={null}
{ "error": "Internal server error" }
```

## Examples

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://admin.telemetron.ai/api/ext-v1/device/bulk-metadata \
    -H "x-api-key: tlm_xxxxxxxxxxxx" \
    -H "Content-Type: application/json" \
    -d '{
      "devices": {
        "SN-A100": { "firmwareVersion": "3.2.1", "status": "active", "location": "Building A" },
        "SN-B200": { "firmwareVersion": "3.2.1", "status": "maintenance" },
        "SN-UNKNOWN": { "status": "active" }
      }
    }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch("https://admin.telemetron.ai/api/ext-v1/device/bulk-metadata", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-api-key": "tlm_xxxxxxxxxxxx",
    },
    body: JSON.stringify({
      devices: {
        "SN-A100": { firmwareVersion: "3.2.1", status: "active", location: "Building A" },
        "SN-B200": { firmwareVersion: "3.2.1", status: "maintenance" },
        "SN-UNKNOWN": { status: "active" },
      },
    }),
  });

  const data = await response.json();
  console.log(data);
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      "https://admin.telemetron.ai/api/ext-v1/device/bulk-metadata",
      headers={
          "Content-Type": "application/json",
          "x-api-key": "tlm_xxxxxxxxxxxx",
      },
      json={
          "devices": {
              "SN-A100": {"firmwareVersion": "3.2.1", "status": "active", "location": "Building A"},
              "SN-B200": {"firmwareVersion": "3.2.1", "status": "maintenance"},
              "SN-UNKNOWN": {"status": "active"},
          }
      },
  )

  print(response.json())
  ```
</CodeGroup>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST https://admin.telemetron.ai/api/ext-v1/device/bulk-metadata \
    -H "x-api-key: tlm_xxxxxxxxxxxx" \
    -H "Content-Type: application/json" \
    -d '{
      "devices": {
        "SN-A100": { "firmwareVersion": "3.2.1", "status": "active", "location": "Building A" },
        "SN-B200": { "firmwareVersion": "3.2.1", "status": "maintenance" },
        "SN-UNKNOWN": { "status": "active" }
      }
    }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch("https://admin.telemetron.ai/api/ext-v1/device/bulk-metadata", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-api-key": "tlm_xxxxxxxxxxxx",
    },
    body: JSON.stringify({
      devices: {
        "SN-A100": { firmwareVersion: "3.2.1", status: "active", location: "Building A" },
        "SN-B200": { firmwareVersion: "3.2.1", status: "maintenance" },
        "SN-UNKNOWN": { status: "active" },
      },
    }),
  });

  const data = await response.json();
  console.log(data);
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      "https://admin.telemetron.ai/api/ext-v1/device/bulk-metadata",
      headers={
          "Content-Type": "application/json",
          "x-api-key": "tlm_xxxxxxxxxxxx",
      },
      json={
          "devices": {
              "SN-A100": {"firmwareVersion": "3.2.1", "status": "active", "location": "Building A"},
              "SN-B200": {"firmwareVersion": "3.2.1", "status": "maintenance"},
              "SN-UNKNOWN": {"status": "active"},
          }
      },
  )

  print(response.json())
  ```
</RequestExample>

<ResponseExample>
  ```json 200 — Success theme={null}
  {
    "success": true,
    "updated": 2,
    "notFound": 1,
    "results": [
      { "identifier": "SN-A100", "deviceId": "d1a2b3c4-...", "status": "updated" },
      { "identifier": "SN-B200", "deviceId": "e5f6a7b8-...", "status": "updated" },
      { "identifier": "SN-UNKNOWN", "deviceId": "", "status": "not_found" }
    ]
  }
  ```

  ```json 400 — Bad Request theme={null}
  {
    "error": "devices is required and must be an object mapping identifiers to metadata"
  }
  ```

  ```json 401 — Unauthorized theme={null}
  {
    "error": "Missing API key"
  }
  ```

  ```json 500 — Internal Server Error theme={null}
  {
    "error": "Internal server error"
  }
  ```
</ResponseExample>

## Status Codes

| Code | Description                                                                  |
| ---- | ---------------------------------------------------------------------------- |
| 200  | Devices successfully updated                                                 |
| 400  | Validation error (missing or invalid fields, invalid JSON, too many devices) |
| 401  | Authentication error (missing or invalid API key)                            |
| 500  | Internal server error                                                        |
