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

# Register External Bodycam Recording

> Add a third-party video URL to an account's bodycam clips without uploading the file.

`POST` `https://api.sonorancad.com/v2/general/bodycam-recordings/external`

> **Rate limit:** `30 requests per minute` per API key, shared with bodycam uploads.

Register a hosted video URL under a community member's bodycam recordings. The clip can then be [selected in a custom record](https://docs.sonoransoftware.com/cad/integration-plugins/in-game-integration/available-plugins/bodycam.md#attach-clips-to-custom-records).

Sonoran CAD saves the URL, without downloading the video. External clip entries do not expire or use your daily recording allowance. The video must remain available at that URL; attaching it to a record does not preserve the file. [Clips uploaded to Sonoran CAD](https://docs.sonoransoftware.com/cad/api-integration/api-endpoints-v2/general/configuration/upload-bodycam-recording.md) still expire after 24 hours.

## Request

Send `application/json` with `Authorization: Bearer YOUR_API_KEY`. The API key determines the community.

| Field | Type | Required | Description |
| - | - | - | - |
| `fileUrl` | string | Yes | Absolute HTTPS URL to a playable video file, not a webpage. Maximum 8,192 characters; embedded credentials are not allowed. |
| `accountUuid` | string (UUID) | One target | Recording owner's account UUID. |
| `communityUserId` | string | One target | Recording owner's community user ID. |
| `roblox` | string | One target | Roblox user ID linked to the recording owner's account. |
| `discord` | string | One target | Discord user ID linked to the recording owner's account. |
| `durationMs` | integer | No | Nonnegative duration in milliseconds. Defaults to `0` (unknown). |
| `identId` | integer | No | Nonnegative identifier ID owned by the target account. Defaults to its selected identifier, when available. |
| `unitNumber` | string | No | Unit number; defaults to the resolved identifier's unit number. Maximum 255 characters. |
| `unitLocation` | string | No | Recording location. Maximum 2,000 characters. |
| `originalFileName` | string | No | Filename saved with the clip; defaults to the URL's filename. Maximum 255 characters. |
| `mimeType` | string | No | Video MIME type, such as `video/mp4`. Inferred from recognized URL extensions when omitted. Maximum 100 characters. |

Provide **exactly one** of `accountUuid`, `communityUserId`, `roblox`, or `discord`. The target must already belong to the community. Use a stable URL that viewers can access without additional sign-in.

## Example Request

The SDKs do not yet include a helper for this endpoint. These examples send JSON directly over HTTP.

> [tab] Sonoran.lua
>
> ```lua theme={null}
> -- Standalone Lua: luarocks install luasec; luarocks install lua-cjson
> local https = require("ssl.https")
> local ltn12 = require("ltn12")
> local json = require("cjson")
>
> local body = json.encode({
>   accountUuid = "USER_ACCOUNT_UUID",
>   fileUrl = "https://clips.example.com/chase.mp4",
>   durationMs = 90000
> })
> local response = {}
> local _, status = https.request({
>   url = "https://api.sonorancad.com/v2/general/bodycam-recordings/external",
>   method = "POST",
>   headers = {
>     ["Authorization"] = "Bearer YOUR_API_KEY",
>     ["Content-Type"] = "application/json",
>     ["Content-Length"] = tostring(#body)
>   },
>   source = ltn12.source.string(body),
>   sink = ltn12.sink.table(response)
> })
> print(status, table.concat(response))
> ```

> [tab] Sonoran.js
>
> ```javascript theme={null}
> // Node.js 18+; uses the built-in fetch API.
> const response = await fetch(
>   'https://api.sonorancad.com/v2/general/bodycam-recordings/external',
>   {
>     method: 'POST',
>     headers: {
>       Authorization: 'Bearer YOUR_API_KEY',
>       'Content-Type': 'application/json'
>     },
>     body: JSON.stringify({
>       accountUuid: 'USER_ACCOUNT_UUID',
>       fileUrl: 'https://clips.example.com/chase.mp4',
>       durationMs: 90000
>     })
>   }
> );
> console.log(response.status, await response.json());
> ```

> [tab] Sonoran.py
>
> ```python theme={null}
> import json
> import urllib.request
>
> request = urllib.request.Request(
>     "https://api.sonorancad.com/v2/general/bodycam-recordings/external",
>     method="POST",
>     headers={
>         "Authorization": "Bearer YOUR_API_KEY",
>         "Content-Type": "application/json",
>     },
>     data=json.dumps({
>         "accountUuid": "USER_ACCOUNT_UUID",
>         "fileUrl": "https://clips.example.com/chase.mp4",
>         "durationMs": 90000,
>     }).encode("utf-8"),
> )
> with urllib.request.urlopen(request) as response:
>     print(response.status, json.load(response))
> ```

> [tab] Sonoran.Net
>
> ```csharp theme={null}
> // .NET 6+; uses HttpClient.
> using System;
> using System.Net.Http;
> using System.Net.Http.Headers;
> using System.Net.Http.Json;
>
> using var client = new HttpClient();
> client.DefaultRequestHeaders.Authorization =
>     new AuthenticationHeaderValue("Bearer", "YOUR_API_KEY");
> using var response = await client.PostAsJsonAsync(
>     "https://api.sonorancad.com/v2/general/bodycam-recordings/external",
>     new
>     {
>         accountUuid = "USER_ACCOUNT_UUID",
>         fileUrl = "https://clips.example.com/chase.mp4",
>         durationMs = 90000
>     });
> Console.WriteLine((int)response.StatusCode);
> Console.WriteLine(await response.Content.ReadAsStringAsync());
> ```

> [tab] OpenAPI
>
> Import this YAML into Postman with **Import → Raw text**.
>
> ```yaml theme={null}
> openapi: 3.0.3
> info:
>   title: Sonoran CAD v2 - Register External Bodycam Recording
>   version: 1.0.0
> servers:
>   - url: https://api.sonorancad.com
> paths:
>   /v2/general/bodycam-recordings/external:
>     post:
>       summary: Register External Bodycam Recording
>       operationId: registerExternalBodycamRecording
>       security:
>         - bearerAuth: []
>       requestBody:
>         required: true
>         content:
>           application/json:
>             schema:
>               type: object
>               required: [fileUrl]
>               oneOf:
>                 - required: [accountUuid]
>                 - required: [communityUserId]
>                 - required: [roblox]
>                 - required: [discord]
>               properties:
>                 fileUrl:
>                   type: string
>                   format: uri
>                   pattern: '^https://'
>                   maxLength: 8192
>                 accountUuid:
>                   type: string
>                   format: uuid
>                 communityUserId:
>                   type: string
>                 roblox:
>                   type: string
>                 discord:
>                   type: string
>                 durationMs:
>                   type: integer
>                   format: int64
>                   minimum: 0
>                   default: 0
>                 identId:
>                   type: integer
>                   minimum: 0
>                   maximum: 2147483647
>                 unitNumber:
>                   type: string
>                   maxLength: 255
>                 unitLocation:
>                   type: string
>                   maxLength: 2000
>                 originalFileName:
>                   type: string
>                   maxLength: 255
>                 mimeType:
>                   type: string
>                   pattern: '^video/[a-zA-Z0-9.+-]+$'
>                   maxLength: 100
>             example:
>               accountUuid: 00000000-0000-4000-8000-000000000001
>               fileUrl: https://clips.example.com/chase.mp4
>               durationMs: 90000
>       responses:
>         '201':
>           description: Recording metadata created; expiresAt is null.
>           content:
>             application/json:
>               schema:
>                 type: object
>                 properties:
>                   id:
>                     type: string
>                     format: uuid
>                   fileUrl:
>                     type: string
>                     format: uri
>                   source:
>                     type: string
>                     enum: [external]
>                   expiresAt:
>                     type: string
>                     format: date-time
>                     nullable: true
>         '400':
>           description: Invalid URL, target identifiers, identifier ownership, or metadata.
>         '401':
>           description: Missing or invalid API key.
>         '404':
>           description: Target account not found or not a member of this community.
>         '429':
>           description: Rate limit exceeded.
> components:
>   securitySchemes:
>     bearerAuth:
>       type: http
>       scheme: bearer
> ```

> [tab] cURL
>
> ```bash theme={null}
> curl --request POST \
>   --url 'https://api.sonorancad.com/v2/general/bodycam-recordings/external' \
>   --header 'Authorization: Bearer YOUR_API_KEY' \
>   --header 'Content-Type: application/json' \
>   --data '{
>     "accountUuid": "USER_ACCOUNT_UUID",
>     "fileUrl": "https://clips.example.com/chase.mp4",
>     "durationMs": 90000
>   }'
> ```

## Response

Returns `201 Created` with the recording metadata:

```json theme={null}
{
  "id": "00000000-0000-4000-8000-000000000002",
  "communityUuid": "00000000-0000-4000-8000-000000000003",
  "accountUuid": "00000000-0000-4000-8000-000000000001",
  "username": "OfficerExample",
  "identId": 123,
  "unitNumber": "1A-12",
  "unitLocation": "",
  "originalFileName": "chase.mp4",
  "mimeType": "video/mp4",
  "fileSizeBytes": 0,
  "durationMs": 90000,
  "fileUrl": "https://clips.example.com/chase.mp4",
  "s3Key": "",
  "createdAt": "2026-10-30T12:00:00Z",
  "expiresAt": null,
  "source": "external"
}
```

## Errors

| Status | Cause |
| - | - |
| `400` | Invalid HTTPS URL, missing or multiple target identifiers, invalid metadata, or an identifier belonging to another account. |
| `401` | Missing or invalid API key. |
| `404` | Target account not found or not a member of this community. |
| `429` | Rate limit exceeded. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.