Overview
Canvas apps have built-in version control. Every time you publish config changes or deploy a new code bundle, an immutable version record is created with:
- A content hash (SHA-256) — like a git commit SHA
- A commit message (required) — describes what changed
- A full config snapshot — serialized as diffable JSON files
- Who published and when
You can list all versions, diff any two versions, download previous bundles, and restore (rollback) to any version instantly.
How versioning works
There are two ways a new version is created:
| Action | What creates the version | What’s captured |
|---|
Publish (POST /v1/apps/{id}/publish) | Config snapshot (sections, meta, auth, theme, etc.) | Content hash of config, message, config as JSON files |
Deploy (POST /v1/apps/{id}/deploy) | Code bundle upload (React/JS build) | Asset manifest with file hashes, message, optional source files |
Both paths create a VersionRecord in the app’s versions array. The version number auto-increments.
Content hashing
When you publish, Mixpeek generates a deterministic SHA-256 hash of your entire config snapshot. This hash changes only when the config actually changes — identical publishes produce the same hash.
v1 → hash: d733b11021fa → "Initial release"
v2 → hash: e186b986ff96 → "feat: add custom HTML search interface"
v3 → hash: d733b11021fa → "revert: back to original config" (same hash as v1!)
Config as diffable files
Your app config is serialized into individual JSON files for each top-level key. This makes version diffs meaningful:
meta.json → {"title": "Product Search", "logo_url": "..."}
theme.json → {"colors": {"primary": "#FC5185"}}
sections.json → [{"type": "search", ...}]
auth_config.json → {"mode": "clerk", "clerk_allowed_providers": [...]}
custom_html.json → "<div id=\"app\">...</div>"
Listing versions
curl https://api.mixpeek.com/v1/apps/$APP_ID/versions \
-H "Authorization: Bearer $API_KEY"
Response:
{
"versions": [
{
"version": 2,
"s3_version_id": "e186b986ff96",
"message": "feat: add custom HTML search interface",
"deployed_by": "int_40ed22c147907235",
"deployed_at": "2026-03-27T13:10:00Z",
"environment": "production"
},
{
"version": 1,
"s3_version_id": "d733b11021fa",
"message": "Initial release",
"deployed_by": "int_40ed22c147907235",
"deployed_at": "2026-03-27T13:09:00Z",
"environment": "production"
}
],
"total": 2
}
Viewing version details
Get full metadata for a specific version, including the source files snapshot and which environments it’s active in:
curl https://api.mixpeek.com/v1/apps/$APP_ID/versions/1 \
-H "Authorization: Bearer $API_KEY"
Response:
{
"version": 1,
"s3_version_id": "d733b11021fa",
"message": "Initial release",
"deployed_by": "int_40ed22c147907235",
"deployed_at": "2026-03-27T13:09:00Z",
"environment": "production",
"source_files": {
"meta.json": "{\"title\": \"Product Search\"}",
"theme.json": "{\"colors\": {\"primary\": \"#FC5185\"}}",
"sections.json": "[]"
},
"is_active": {
"staging": false,
"production": true
}
}
Diffing versions
Compare any two versions to see what changed — like git diff v1..v2:
curl https://api.mixpeek.com/v1/apps/$APP_ID/versions/1/diff/2 \
-H "Authorization: Bearer $API_KEY"
Response:
{
"app_id": "app_abc123",
"from_version": 1,
"to_version": 2,
"summary": {
"added": 1,
"removed": 0,
"modified": 0,
"unchanged": 7
},
"source_diff": {
"custom_html.json": "--- v1/custom_html.json\n+++ v2/custom_html.json\n@@ -0,0 +1 @@\n+\"<div><h1>Custom Search</h1></div>\""
}
}
The summary counts file-level changes (based on content hashes). The source_diff provides unified diffs of the actual content — the same format as git diff.
Rollback
Quick rollback (one level)
Restore the previous published config instantly:
curl -X POST https://api.mixpeek.com/v1/apps/$APP_ID/rollback \
-H "Authorization: Bearer $API_KEY"
Restore any version
For deploy-based versions (code bundles), restore any specific version to any environment:
curl -X POST https://api.mixpeek.com/v1/apps/$APP_ID/versions/3/restore \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"environment": "production"}'
This is instant — no rebuild required. The previous assets are always retained in S3.
Download a version’s bundle
Download the original zip bundle for any deployed version and work locally:
Download the bundle
curl https://api.mixpeek.com/v1/apps/$APP_ID/versions/2/download \
-H "Authorization: Bearer $API_KEY"
Response:{
"download_url": "https://s3.amazonaws.com/...",
"version": 2,
"asset_prefix": "apps/my-app/dep_abc123",
"expires_in": 3600
}
Download and extract
curl -o bundle.zip "$DOWNLOAD_URL"
unzip bundle.zip -d my-app/
Edit files locally
Make your changes to the extracted source.
Re-zip and deploy
cd my-app && zip -r ../updated.zip .
# Upload and deploy as usual (see Deploy from Code)
When deploying via CI/CD or the CLI, you can attach git metadata to each version for full traceability:
curl -X POST https://api.mixpeek.com/v1/apps/$APP_ID/deploy \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source": "cli_upload",
"bundle_s3_key": "$BUNDLE_KEY",
"message": "fix: search results pagination",
"git_commit_sha": "a1b2c3d4e5f6",
"git_commit_message": "fix: search results pagination",
"git_author": "Jane Smith <jane@example.com>"
}'
Git metadata appears in version detail responses and Studio’s version history panel.
If you connect a GitHub repository via POST /v1/apps/{id}/connect-repo, deploys are triggered automatically on push. Git metadata is captured from the webhook payload — no manual fields needed.
Version record fields
| Field | Type | Description |
|---|
version | int | Auto-incrementing version number |
s3_version_id | string | Content hash (publish) or S3 version ID (deploy) |
asset_prefix | string | S3 path prefix for deployed assets |
asset_manifest | object | Map of {relative_path: {s3_key, hash, size}} |
source_files | object | Map of {path: content} for source-level diffs |
deployed_by | string | Internal ID of the user who published/deployed |
deployed_at | string | ISO 8601 timestamp |
environment | string | "staging" or "production" |
message | string | Commit message (required) |
build_duration_ms | int | Build time in milliseconds (deploy only) |
git_commit_sha | string | Git SHA (if provided) |
git_commit_message | string | Git commit message (if provided) |
git_author | string | Git author (if provided) |