> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.cargovision.app/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.cargovision.app/_mcp/server.

# Edge device integration

Edge devices run detection at the inspection point and push results to the API. Edge endpoints use [API key authentication](/authentication#edge-device-api-keys) and address containers by container number, not record ID.

| Method | Endpoint                                               | Purpose                                                                                           |
| ------ | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------- |
| `POST` | `/v1/edge/containers/identify`                         | Register an OCR-confirmed container number                                                        |
| `POST` | `/v1/edge/containers/{containerNumber}/scans/defect`   | Submit a completed defect scan                                                                    |
| `POST` | `/v1/edge/containers/{containerNumber}/scans/category` | Submit a completed cargo-category scan                                                            |
| `POST` | `/v1/edge/containers/{containerNumber}/scans/illegal`  | Submit a dangerous-goods scan; currently parked, so use it only when that product path is enabled |
| `POST` | `/v1/tracking-data/stream-events`                      | Report camera stream lifecycle changes                                                            |

Request bodies are JSON with a 50 MB limit. Send images as base64 JPEG, either raw or as a `data:` URL.

## Send one request per scan session

Don't send a request per frame. Track objects across frames, keep the highest-confidence detection for each tracked object, and submit once when the session ends. A session usually ends when no new objects appear for a few seconds, when OCR reads a different container, or when an operator completes the scan manually.

```mermaid
sequenceDiagram
  participant Edge as Edge device
  participant API as Cargovision API
  Edge->>API: POST /v1/tracking-data/stream-events (STARTED)
  Edge->>API: POST /v1/edge/containers/identify
  API-->>Edge: 200 containerId, status IDENTIFIED
  Edge->>API: POST /v1/edge/containers/{containerNumber}/scans/defect
  API-->>Edge: 200 graded defect summary
  Edge->>API: POST /v1/edge/containers/{containerNumber}/scans/category
  API-->>Edge: 200
```

## 1. Identify the container

Send the container number once OCR has read it consistently across frames.

```bash
curl -sS -X POST https://api.cargovision.app/v1/edge/containers/identify \
  -H "X-API-Key: $CARGOVISION_EDGE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "container_number": "MSCU1234566",
    "image": "<base64 JPEG>",
    "confidence": 0.94,
    "camera_id": "gate-1-rear-ocr",
    "device_id": "jetson-gate-1",
    "user_email": "operator@example.com"
  }'
```

| Field                                 | Required | Notes                                                                                                                                                                                                          |
| ------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `container_number`                    | Yes      | ISO 6346 format with a valid check digit. Invalid numbers return `400 INVALID_CONTAINER_NUMBER`.                                                                                                               |
| `image`                               | Yes      | The frame the number was read from                                                                                                                                                                             |
| `confidence`, `box`, `detections`     | No       | OCR confidence, region, and raw detections                                                                                                                                                                     |
| `camera_id`, `device_id`, `timestamp` | No       | Provenance for the audit trail                                                                                                                                                                                 |
| `user_email`                          | No       | The operator who started the session. A new container is owned by this user; without it the container belongs to the autonomous edge account. Ownership is set by the first push and never changes afterwards. |

The response tells you whether the container is new:

```json
{
  "success": true,
  "message": "Container created and identified",
  "data": {
    "containerId": "665f1c2e9b1e4a0012ab34cd",
    "containerNumber": "MSCU1234566",
    "isNewContainer": true,
    "imageUrl": "https://…",
    "status": "IDENTIFIED"
  }
}
```

## 2. Submit the defect scan

```json
{
  "detections": [
    { "class_name": "dent", "confidence": 0.91, "box": { "x1": 0.12, "y1": 0.40, "x2": 0.25, "y2": 0.58 } },
    { "class_name": "hole", "confidence": 0.83, "box": { "x1": 0.61, "y1": 0.22, "x2": 0.66, "y2": 0.29 } }
  ],
  "box_format": "normalized_xyxy",
  "image": "<base64 JPEG>",
  "visualization": "<optional annotated base64 JPEG>",
  "camera_id": "gate-1-defect",
  "device_id": "jetson-gate-1"
}
```

* `detections` is required. Send an empty array for a clean scan.
* Send boxes as `x1`, `y1`, `x2`, `y2`. Set `box_format` to `normalized_xyxy` when coordinates are fractions of the frame size between 0 and 1.
* Send `class_name`, `confidence`, and `box` only. The API assigns severity, structural integrity, and the AI decision; any `severity` you send is ignored. See [Inspection lifecycle](/inspection-lifecycle#defect-grading).
* If the container hasn't been identified yet, the API creates it.

## 3. Submit the category scan

The category scan carries segmentation results for the cargo. `detections` and `image` are required. Each detection can include `polygon` (`x` and `y` arrays), `area_percentage`, `quantity`, and `volume`. When the container has a manifest, the API checks the detected categories against it.

## Report stream health

Send a stream event whenever a camera stream changes state so the dashboard can show stream health:

```json
{
  "camera_id": "gate-1-defect",
  "device_id": "jetson-gate-1",
  "event_type": "RECONNECTING",
  "rtsp_url": "rtsp://camera.local/stream1",
  "model_key": "container_defect",
  "error_message": "Read timeout"
}
```

`camera_id` and `event_type` are required. `event_type` is one of `STARTED`, `STOPPED`, `ERROR`, or `RECONNECTING`. Credentials in `rtsp_url` are stripped before storage.

> **Warning**
>
> `POST /v1/tracking-data` and `POST /v1/tracking-data/batch` are deprecated per-frame telemetry endpoints. Don't build new integrations on them; container scans belong under `/v1/edge/*`.

## Handle failures

* `400` means the request will never succeed as sent. Fix the payload; don't retry it unchanged.
* `401` means the API key is missing or wrong.
* `5xx` and network errors are transient. Retry with backoff, and log locally instead of blocking inference.