bota_enc_v2 recording
ciphertext unchanged. Apps relay the authorization, ciphertext, manifest, and
completion receipt as opaque bytes. The backend verifies and decrypts the
ciphertext in an isolated worker before publishing the final OGG recording.
Authentication
React Native applications can use the SDK’s managed native backend adapter in App SDK2.0.0-beta.11 through createEncryptedUploadV2Backend.
It owns session persistence, manifest
replay, verification polling and receipt delivery. Configure your authenticated
backend URL, fresh app-token callback, exact identity and cancellation scope;
invoke sync again after reopening/reconnecting. See
Client SDKs.
The existing beta.10 package does not contain this helper. A backend proxy
exposing only legacy upload-complete must expose the v2 routes; upgrading
the package alone does not migrate a legacy integration.
All operations accept a secret API key, a restricted key with
recordings:write, or the device token bound to the exact device and end user.
Every recording, device, and session lookup is scoped to the authenticated
project.
The first-party Cognito application has an equivalent dashboard bridge under
/dashboard/projects/{projectId}/recordings/{recordingId}/encrypted-upload-v2/sessions.
It checks organization ownership of the project and uses the same scoped
session services. Customer mobile apps must not embed secret API keys; keep
their session provider on their own authenticated backend.
Lifecycle
- Create a session and receive a signed 408-byte authorization plus a private, checksum-bound staging target.
- Before delivering that authorization to the device, establish a fresh upload-only credential context using the device’s independent context nonce and opaque proof. This does not change pairing.
PUTthe device ciphertext directly to the returned URL with every returned header exactly as supplied.- Submit the exact authenticated 580-byte manifest.
- Poll session status while the worker verifies, decrypts, validates OGG, and publishes the final recording.
- Reacquire fresh context and relay the completion receipt. Delete the device
recording only after status is
publishedand the device has verified the signed 336-byte completion receipt for the exact recording and generation.
202 manifest response, or processing state is never
device-deletion authorization.
Establish upload context
POST /v1/devices/{id}/encrypted-upload-v2/contexts
201 response contains context_id (UUID), state: "challenge",
challenge_base64 (196 opaque bytes), result_base64: null, and expires_at.
Relay the challenge unchanged to the device. The device verifies it and returns
an encrypted proof of its installed token; the app never supplies or opens the
token. This nonce is separate from auth_nonce_base64 and Grant nonces.
POST /v1/devices/{id}/encrypted-upload-v2/contexts/{context_id}/proof
202 with state: "pending", or 200 with
state: "complete". Poll
GET /v1/devices/{id}/encrypted-upload-v2/contexts/{context_id} until complete,
then relay the 264-byte result_base64 unchanged. The backend worker checks the
actual credential against the current confirmed binding and rejects revoked,
provisional, cross-device or stale credentials and active factory resets.
All three operations have a first-party dashboard equivalent under
/dashboard/projects/{projectId}/devices/{deviceId}/encrypted-upload-v2/contexts.
A device token may use me in the API path, only for that exact device.
The challenge expires after 60 seconds; the device permits at most 30 seconds
for the whole exchange. Polling and identical retries never extend either
deadline. Retrying create with the same nonce returns the exact challenge;
retrying the same proof repairs a lost enqueue. A different proof conflicts.
Expired/failed contexts require a new device-generated attempt. Unknown fields
and noncanonical base64 are rejected. Never substitute app time or trust keys.
At most eight unexpired contexts may coexist for a device. Capacity exhaustion
returns 429 (rate_limit_exceeded); retry with bounded backoff, respecting
the original attempt deadline. If that deadline expires, start a fresh device
attempt. Keep the recording and its session identity while waiting, including
when publication succeeded but fresh context for receipt validation is pending.
Create a session
POST /v1/recordings/{id}/encrypted-upload-v2/sessions
The recording must be a pending device recording whose immutable device,
recording UUID, generation, and binding generation match the request.
Unknown fields are rejected.
Submit the manifest
Recover the staging target before submission
POST /v1/recordings/{id}/encrypted-upload-v2/sessions/{session_id}/staging-url
If an app retained session identifiers but no presigned URL, request the same
target with this body:
200 response contains url, method: "PUT", and required headers, in
the same shape as the session’s staging object. It re-signs the original
checksum-bound ciphertext object only. It does not create a new session or
recipient, change owner, or extend the original 15-minute expiry. Renewal
requires staging state and unchanged live binding generation, pending
recording identity, policy and configuration. A stale context returns 409.
After manifest acceptance, reconcile session status instead of re-uploading.
Replace a pre-manifest session (capability gated)
POST /v1/recordings/{id}/encrypted-upload-v2/sessions/{session_id}/recover
reason is "expired" (default when omitted),
"nonce_changed", or "channel_changed". Keep the default for expiry recovery. On reconnect or device reboot,
nonce_changed permits recovery before expiry only when the supplied device
nonce differs from the immutable signed authorization and no manifest was ever
accepted. The required capability mask is 0x37f, including upload context
and recovery. This is not an app assertion that device admission never happened:
the device must independently validate the signed replacement, current nonce,
fresh authenticated context, increasing owner revision and absence of receipts.
The response identifies the durable successor:
0x0008 and a higher owner revision. The recording and
ciphertext remain unchanged; the recipient key, session and staging object are
new. Do not erase device or app evidence to force a retry.
Do not confuse the binary document version (2) with its transport profile:
BOTAAUT2 byte 13 is profile 3, and byte 14 is storage format 3 for both
initial and replacement authorizations.
Only a pre-manifest session satisfying the selected reason is eligible. Current
project/device/binding/recording/policy checks still apply. Failed, cancelled,
processing and published sessions require reconciliation, not replacement.
An identical parent retry returns its existing successor; it never creates a
second child or extends the old authorization. If that child also expired or
has a stale nonce, follow that exact child and use the applicable reason within
a bounded retry limit. Never refresh its nonce in place. Accepted-manifest/processing/published sessions must
resume status reconciliation rather than replacement or restaging.
Direct WiFi/cellular handoff uses reason: "channel_changed" together with
channel: "wifi" or "cellular". Both parent and target must be different
direct channels; this does not take over a BLE owner. Other reasons must omit
channel. Close the previous transport before handoff. Same-channel retries
retain the session and renew staging credentials; handoff creates the signed
successor described above and restarts ciphertext from byte zero.
Provisioned devices can reconcile lost create/recovery responses with
POST /v1/devices/me/encrypted-upload-v2/reconcile, authenticated by their
device token. The request uses the identity fields below. It returns null or
the recording/session/revision lookup, plus device_id and the actual direct
channel. A WiFi caller can discover its cellular owner and vice versa, but
this lookup does not authorize upload, handoff or deletion. Firmware direct-v2
remains disabled by default pending transport and hardware acceptance.
The direct firmware coordinator distinguishes transient network/pending-worker
results from terminal authorization or integrity failures. Context retries
reuse the same nonce and original deadline; successful HTTP transfer still
does not permit deleting the local recording without its signed receipt.
The October 2 recovery source update schedules transient results after 30, 60,
120, then 300 seconds, with further attempts capped at 300 seconds. A 30-second
timer checks eligibility, so actual starts can be later. Bluetooth preference,
another upload or worker allocation failure defers the attempt without losing
it; reset cancels it. Each attempt releases its radio ownership after a bounded
publication check. Restart restores the durable recording/session identity;
the in-memory backoff restarts. This update still requires firmware rollout and
physical interruption qualification; default direct-v2 gates remain unchanged.
The dashboard bridge exposes the same /recover action. Recovery does not
change pairing, permit plaintext fallback or authorize deletion. Deletion still
requires a published, exact-session signed receipt verified by the device.
Reconcile a lost creation response (first-party dashboard)
The organization-authorized dashboard providesPOST /dashboard/projects/{projectId}/devices/{deviceId}/encrypted-upload-v2/reconcile.
This is not an additional public API-key endpoint. Send the create-session
identity fields except device_id (owned by the path) and auth_nonce_base64:
binding_generation, recording_uuid, recording_generation, storage_format,
channel, capabilities_base64, ciphertext_length and ciphertext_sha256.
After current tenant/binding and exact physical recording checks, 200
returns null when no matching recording exists, or an identity-only result:
session_id and owner_revision are both null when the recording committed
but no session did. When a session exists, exact ciphertext/channel/profile
checks must also pass before returning the latest retained owner. Deleted or
conflicting identities fail closed. The result contains no signed document,
receipt, key or staging URL and is not upload/deletion authority. Reconcile an
uncertain create before retrying, including after a concurrent-create conflict;
do not invent a new recording UUID or reset terminal history to revision 1.
Submit the authenticated bytes
POST /v1/recordings/{id}/encrypted-upload-v2/sessions/{session_id}/manifest
A newly accepted manifest returns
202; an exact replay after publication
returns 200.
state is ready, processing, or published. Conflicting bytes or a stale
owner revision fail closed.
For an uncertain PUT or manifest response, reconcile the saved session first.
If it is ready or processing, poll that session; if published, obtain its
signed receipt. If it remains before manifest acceptance and the saved owner
is still valid, replay the exact saved manifest before repeating the PUT.
An accepted replay proceeds to status polling without uploading bytes again.
The recovery source update adds 409 encrypted_upload_v2_staging_missing when
S3 affirmatively reports that the session’s object is absent. Only this result
permits a fresh staging URL and whole-object PUT for that same owner. Timeouts,
403, 503, or an unrelated 409 do not prove absence. An expired/replaced owner
still requires the existing signed recovery procedure. Deploy the backend
update before firmware that relies on the new error; older backends may return
503 for an absent object and the new firmware retains the file while retrying.
Get session status
GET /v1/recordings/{id}/encrypted-upload-v2/sessions/{session_id}
created, staging, staged, ready, processing,
published, failed, cancelled, and expired. Plaintext fields appear only
after the manifest establishes them. Receipt and publication fields appear
only for published sessions. The receipt is canonical base64 for exactly 336
bytes and remains valid for 30 days from issuance.
Cancel a session
DELETE /v1/recordings/{id}/encrypted-upload-v2/sessions/{session_id}
204 and is allowed only before worker processing owns
the session. It schedules idempotent cleanup of staged ciphertext and live
one-time recipient material; it never deletes the device recording.
Errors
Cryptographic failures use stable public errors and do not expose private
verification details.

