Skip to main content

How to retrieve body worn recordings via the integration API

This guide covers how to receive, list, and download body worn recordings in your content destination application using the body worn content destination API.

The full reference and an example server implementation are in the body worn content destination API repository. This guide covers the retrieval workflow and links there for the full field and command reference.

Prerequisites​

  • A body worn system (BWS) connected to your content destination via a connection file.
  • Your content destination server implements the body worn content destination API (modeled on the OpenStack Swift API).
  • The connection file credentials (BlobAPIUserName and BlobAPIKey) and at least one AuthenticationTokenURI configured.
  • HTTPS certificate trust established between the system controller and your server. The HTTPSCertificate field in the connection file must contain a valid Base64-encoded X.509 certificate. HTTP without TLS is only appropriate for development.
  • At least one body worn camera docked and offloading recordings.

Connect to the body worn content destination API​

The BWS pushes recordings to your server. Your application acts as the content destination (the server), and the BWS is the client. Authentication uses the OpenStack Swift Keystone method.

When the BWS needs to upload, it sends a GET to your auth endpoint with credentials from the connection file:

curl --request GET \
--anyauth \
--user "<username>:<password>" \
--http1.1 \
--header "X-Auth-User: bwsintegration" \
--header "Auth-Key: s3cr3tAPIk3y" \
http://your-cd-server.example.com/auth/v1.0

Your server responds with a storage URL and auth token:

HTTP/1.1 200 OK
X-Auth-Token: tk-9a4f2c8b1e3d
X-Storage-Url: https://your-cd-server.example.com/v1/AUTH_bwsintegration

The BWS uses X-Auth-Token in all subsequent PUT and POST requests. Your server must validate this token on every request and return 401 Unauthorized if it's missing or expired.

List available recordings​

Each recording the BWS uploads creates a container named using this pattern:

<UserID>_<BWCSerialNumber>_<TriggerOnTime>

TriggerOnTime is in UTC, formatted as YYYYMMDDTHHMMSSZ. For example:

f47ac10b-58cc-4372-a567-0e02b2c3d479_ACCC8EF12345_20260415T143022Z

Inside each recording container, the BWS uploads the video clips plus optional companion objects (a .key object per clip when encryption is on, a bookmark object per bookmark, and a GNSS trail JSON when location recording is on), alongside System, Users, and Devices entries that describe the source. For the exact object layout and naming, see the storage object structure in the README.

To know when a recording is fully transferred, check the Status metadata attribute on the container. It transitions from Transferring to Complete. Once Complete, no further uploads happen to that container.

If you use the example server implementation, a zero-sized file named complete appears in the container directory when transfer finishes. You can watch for this file (using inotify or similar) instead of polling metadata.

Download a recording with metadata​

All metadata arrives as HTTP headers on the PUT and POST requests the BWS makes to your server. Header keys use the prefix X-Container-Meta- for recording-level metadata and X-Object-Meta- for clip-level metadata. The first letter after each hyphen is capitalized.

The tables below show an illustrative subset, not the full set. For the complete list of container and object headers, see the metadata reference in the README.

Recording container metadata​

A few representative headers the BWS sends when it creates or updates a recording container:

Header keyDescription
X-Container-Meta-StatusTransferring or Complete
X-Container-Meta-TriggeronReason the recording started
X-Container-Meta-StarttimeisoActual recording start including prebuffer (RFC 3339 UTC)

Video clip object metadata​

A few representative headers on each video clip object:

Header keyDescription
X-Object-Meta-StarttimeisoClip start (RFC 3339 UTC)
X-Object-Meta-ContainertypeFile format: mkv or mp4

Store this metadata alongside the binary content. The metadata values are URL-encoded UTF-8 strings, with per-field byte limits defined in the README. Don't assume a fixed width; read the reference for the exact limits.

GNSS location data​

If you declared the StoreGNSSTrackRecording capability, the BWS uploads a companion JSON object with the recording, named <date>_<time>_<id>_<bwcid>_gpstrail.json. It holds an array of coordinate entries, each a WKT POINT(longitude latitude), seconds from the recording start, and a timestamp:

{ "LocationWKT": "POINT(13.221184 55.718409)", "SecondsFromStart": 64.0, "Timestamp": "2022-08-23T11:48:10Z" }

For the full schema, see the GNSS track description in the README.

Verify digital signature (chain of custody)​

Signed video embeds a cryptographic checksum into each video frame, signed by the camera's unique device ID. This lets you verify that a recording hasn't been tampered with after it left the camera.

Enable signed video support​

Declare the StoreSignedVideo capability in your System/Capabilities.json:

{
"StoreSignedVideo": true
}

To find the key in the capability document and how to declare other capabilities, see the capabilities reference in the README. The BWS then enables signed video on connected cameras, and the signature data is embedded directly in the video stream frames.

Verify a recording​

Signed video verification confirms two things:

  1. The recording originated from a specific Axis body worn camera (authenticity).
  2. No frames have been added, removed, or modified since recording (integrity).

To verify exported recordings, use AXIS File Player. The player shows a verification indicator: a green checkmark means the recording hasn't been tampered with. Right-click the video and select "Show digital signature" to inspect the metadata.

For programmatic verification in your content destination, the signature data is embedded in the video container. Your application must preserve the original byte stream when storing and exporting. Any re-encoding or frame manipulation invalidates the signature.

Encryption and decryption​

If you set WantEncryption to true and provided a PublicKey in the connection file, all video clips and GNSS data arrive encrypted. Each encrypted clip has a companion .key object that holds the content key wrapped with your RSA public key.

Decryption is a two-step unwrap. First, read the EncryptedKey value from the .key object and unwrap it with your RSA private key to recover the content key. Then decrypt the clip with AES-256-CBC, using that content key and the initialization vector (IV) from the same .key object. The PublicKeyId in the .key metadata tells you which key pair the BWS used, so you can pick the matching private key.

For the exact commands, see EXAMPLE.md and the decrypt_file.sh script in the repository.

Once encryption is enabled for a system, it can't be turned off.

Troubleshooting​

BWS returns 401 Unauthorized on every request

Your auth endpoint isn't returning valid X-Auth-Token and X-Storage-Url headers. Verify that the BlobAPIUserName and BlobAPIKey in the connection file match what your server expects.

Recordings show status Transferring indefinitely

The BWS hasn't finished uploading all clips. Check that the system controller has network connectivity to your server and that your server responds with 201 Created for each PUT. If the system controller storage fills up, it can't buffer new recordings.

Container metadata is missing fields

Recordings made on older AXIS OS versions might not include all metadata fields from the current specification. Fields can be absent or contain default values. Don't treat missing optional fields as errors.

Recordings rejected with 400 Bad Request

Your server returned this when it can't store the recording permanently. Common causes: the UserID or BWCSerialNumber in the metadata doesn't match any registered user or device. Re-enable disabled users to receive their pending recordings.

Can't decrypt video files

Verify you're using the private key that corresponds to the PublicKey in the connection file. The PublicKeyId in the .key object metadata identifies which key pair was used. The connection file's PublicKey is a Base64-encoded PEM RSA public key.

Time mismatch prevents transfers

The BWS and content destination must use synchronized clocks. A time difference causes the system controller dashboard to show "Can't transfer recordings." Use the same NTP server for both.