Storage Profiles
A storage profile tells Native Frame where a project's media for a given surface should live. One API family — /program/api/v2/storage-profiles/{surface} — configures storage for every surface in one of two modes:
managed— Native Frame provisions and runs a per-project Backblaze B2 bucket for you. No credentials to manage; you get a built-in object listing, presigned downloads, and scoped download keys.byo("bring your own") — you point the surface at your own S3-compatible destination. You keep full control of the bucket; Native Frame writes to it.
The unified storage profile API supersedes the storage sections of the individual features. The Recording destination is now the archive surface, and snapshots and clips each carry their own destination rather than reusing the recording bucket.
Surfaces and modes
| Surface | managed | byo | Notes |
|---|---|---|---|
snapshots | ✅ | ✅ | Managed unlocks the snapshot gallery + download keys. |
archive | ❌ (422) | ✅ | A facade over the Recording config. Encoding (formats/fileType/path) still lives there; the profile owns the S3 destination. |
assets | ✅ | ✅ (publicUrlBase) | Project-level default; a per-upload storageMode still overrides it. |
clips | ❌ (409) | ✅ | Clips carry their own destination. Managed capture is on the roadmap. |
This guide focuses on snapshots and managed storage; the same calls work for every surface.
Authentication
Everything below is backed by the Program Service HTTP API. Base path: /program/api/v2. Every call requires a Bearer JWT whose audience/role includes service-account; the project is taken from the token's projectID claim. See the Program API reference (the Storage section under Program).
Enable managed snapshot storage
PUT /program/api/v2/storage-profiles/{surface} — see Enable managed storage for a surface.
PUT /program/api/v2/storage-profiles/snapshots
Authorization: Bearer <jwt>
Content-Type: application/json
{ "mode": "managed" }
The first call lazily provisions a per-project B2 bucket and may take a few seconds; the profile comes back with status: "provisioning" and transitions to ready. The call is idempotent — repeating it returns the existing profile.
Check a profile's status
GET /program/api/v2/storage-profiles/{surface} — see Get the project's storage profile for a surface. Returns 200 with the profile, or 404 when the surface has no storage configured.
{
"projectId": "proj_123",
"surface": "snapshots",
"mode": "managed",
"status": "ready",
"provider": "backblaze",
"bucketName": "...",
"region": "...",
"endpoint": "...",
"createdAt": "2026-06-18T00:00:00Z",
"updatedAt": "2026-06-18T00:00:05Z"
}
A profile is a summary — it never includes access or secret keys.
Capturing snapshots
Snapshot capture is controlled per stream by a snapshot rule, independent of where the snapshots are stored:
POST /program/api/v2/streams/{id}/snapshot-rule— Create or update the stream's snapshot ruleGET /program/api/v2/streams/{id}/snapshot-rule— Get the stream's snapshot ruleDELETE /program/api/v2/streams/{id}/snapshot-rule— Delete the stream's snapshot rule
Captured snapshots land in whatever storage the snapshots surface is configured to use.
List captured snapshots (managed)
GET /program/api/v2/streams/{id}/snapshots — see List a stream's captured snapshot objects.
GET /program/api/v2/streams/<streamId>/snapshots?limit=50
Authorization: Bearer <jwt>
{
"objects": [
{
"key": "snapshots/<streamId>/2026-06-18T00-00-00.jpg",
"sizeBytes": 84213,
"lastModified": "2026-06-18T00:00:00Z",
"downloadUrl": "https://...",
"expiresAt": "2026-06-18T00:10:00Z"
}
],
"nextCursor": "..."
}
Each downloadUrl is a time-limited presigned GET (valid for minutes) — render a gallery straight from these, with no storage login or raw credentials. Paginate with ?limit= and ?cursor= (pass back nextCursor).
This list is for managed snapshot storage. If the surface is BYO, you access your own bucket directly and the list comes back empty.
Programmatic access with download keys
For tenants who want aws s3 ls/cp access to managed storage, mint a bucket-scoped, read-only key.
POST /program/api/v2/storage-profiles/{surface}/download-key — see Mint a scoped read-only download key.
{
"keyId": "...",
"applicationKey": "shown-once",
"bucketName": "...",
"s3Endpoint": "...",
"region": "...",
"expiresAt": "2026-07-18T00:00:00Z"
}
applicationKey is returned once and is not retrievable later — capture it at creation time. The per-project master key is never exposed.
Revoke a key with DELETE /program/api/v2/storage-profiles/{surface}/download-key/{keyId} (204) — see Revoke a download key. Download keys are managed-only; requesting one on a BYO surface returns 422.
Bring your own storage
Point a surface at your own S3-compatible bucket with mode: "byo":
PUT /program/api/v2/storage-profiles/snapshots
Authorization: Bearer <jwt>
Content-Type: application/json
{
"mode": "byo",
"destination": {
"provider": "aws",
"bucket": "my-snapshots",
"region": "us-east-1",
"accessKey": "<access-key>",
"secretKey": "<secret-key>",
"pathTemplate": "snapshots/{streamId}/"
}
}
- Secrets are stored in our secret manager and are never echoed back.
endpointis optional only for Amazon S3 (the region derives it); every other provider requires it. See the Recording guide for the per-provider endpoint URLs and minimum IAM permissions.
Seed BYO storage from your recording config
If you already have a Recording (archive) destination configured, you can copy it onto another surface instead of re-entering credentials:
POST /program/api/v2/storage-profiles/{surface}/from-archive — see Configure a surface's BYO storage from the archiving config.
List all configured surfaces
GET /program/api/v2/storage-profiles — see List the project's storage profiles across all surfaces. Returns { "profiles": [...] }; unconfigured surfaces are omitted and credentials never appear.
Disable a surface's storage
DELETE /program/api/v2/storage-profiles/{surface} — see Disable / release a surface's storage. For managed surfaces this tears down the provisioned bucket.
Status codes
| Code | Meaning |
|---|---|
400 | Invalid mode, or a missing destination field (e.g. BYO assets without publicUrlBase). |
404 | storage_profile_not_found — the surface has no configured storage. |
409 | managed requested on a surface that doesn't provision it (clips today), or byo on a facade surface via the generic path. |
422 | managed on archive, or a download key requested on a BYO surface. |
503 | This deployment doesn't offer managed storage, or provisioning previously failed — retry or contact support. |
Notes
- A surface needs its own profile. Since the snapshot/clip → archive fallback was removed, a surface with no profile returns
422when something tries to use it. managedprovisioning is lazy and idempotent — safe to call on every "enable" action.- The
archivesurface and the Recording config read and write the same destination; configure either one.