Downloads and sidecar manifests
A finished signing job publishes the signed asset. You fetch it with a short-lived download URL,
check the bytes, and save it. For most formats the C2PA manifest is inside the file. For the
sidecar media types it is a separate .c2pa file that you
download the same way and keep next to the asset.
Request a download
Section titled “Request a download”POST /v1/assets/{asset_id}/download needs the
assets:read scope and the job’s asset_id. The response describes one object:
| Field | Description |
|---|---|
method, url |
The request to make for the bytes. The URL is short-lived. |
headers |
Send exactly these headers, and nothing else, with that request. |
expires_at |
When the URL stops working. Ask for a new one after that. |
media_type |
The media type of the published bytes. |
byte_length, sha256 |
The exact length and lowercase SHA-256 of the published bytes. |
storage_version_sha256 |
An identifier of the stored version, for support requests. |
filename |
The name to save it under: the asset id plus the format extension. |
sidecar_manifest |
Present only when the manifest travels beside the asset. |
The URL is a storage URL, not an API URL. Do not send your Authorization header to it.
Check the bytes
Section titled “Check the bytes”After the download, compare the length and SHA-256 of what you received with byte_length and
sha256. Discard the file if either differs, and request a new download.
curl -sS -X POST "$OP_API_BASE_URL/v1/assets/$ASSET_ID/download" \ -H "Authorization: Bearer $OP_API_KEY" > download.json
HEADERS=()while IFS= read -r header; do HEADERS+=(-H "$header"); done \ < <(jq -r '.headers | to_entries[] | "\(.key): \(.value)"' download.json)
FILENAME=$(jq -r .filename download.json)curl -sS "${HEADERS[@]}" -o "$FILENAME" "$(jq -r .url download.json)"test "$(shasum -a 256 "$FILENAME" | cut -d' ' -f1)" = "$(jq -r .sha256 download.json)"Save the sidecar manifest
Section titled “Save the sidecar manifest”When the response has sidecar_manifest, the asset bytes are exactly the bytes you uploaded and
its C2PA manifest is a separate application/c2pa object. sidecar_manifest has the same fields
as the asset download: method, url, headers, expires_at, media_type, byte_length,
sha256, storage_version_sha256 and filename. Its filename is the asset’s own filename
plus .c2pa.
if jq -e .sidecar_manifest download.json > /dev/null; then MANIFEST=$(jq -r .sidecar_manifest.filename download.json) SIDECAR_HEADERS=() while IFS= read -r header; do SIDECAR_HEADERS+=(-H "$header"); done \ < <(jq -r '.sidecar_manifest.headers | to_entries[] | "\(.key): \(.value)"' download.json) curl -sS "${SIDECAR_HEADERS[@]}" -o "$MANIFEST" "$(jq -r .sidecar_manifest.url download.json)" test "$(shasum -a 256 "$MANIFEST" | cut -d' ' -f1)" = "$(jq -r .sidecar_manifest.sha256 download.json)"fiKeep the two files together and keep both names. The manifest proves nothing without the exact bytes it binds: its hard binding covers the whole asset file, so any change to the asset breaks it, and a manifest separated from its asset cannot be checked at all.
Verify a sidecar pair
Section titled “Verify a sidecar pair”To verify a sidecar asset this service signed, submit the asset’s object id to
POST /v1/verifications as for any other format.
Send only the object id. The API finds the stored manifest itself.
To verify a pair that another product signed, upload both files and send both object ids:
object_id for the asset and manifest_object_id for the .c2pa. See
Verify a pair another product signed.
Which formats use a sidecar
Section titled “Which formats use a sidecar”GET /v1/capabilities is the authority for what a
deployment accepts now. Each sidecar family signs with its own sign-only recipe, which appears in
recipes[] while at least one of its formats is enabled. Sidecar formats carry no watermark. The
reviewed list is in the introduction.
