Snapshots API¶
Content versioning and historical access.
Base URL: {base-url}/snapshots
Authentication: Required (JWT)
Overview¶
The snapshot system enables versioned data queries where every entity (domains, trails, concepts, sparks, etc.) is linked to a specific snapshot version. This allows you to:
- Preview draft content before promoting it to production
- Query historical versions of your content
- Rollback to previous snapshots instantly
- A/B test different content versions
- Maintain data consistency across all entities in a snapshot
How It Works¶
- Each entity has a
snapshot_idcolumn linking it to a specific snapshot version - The
current_snapshottable points to the active published snapshot - Discovery queries automatically filter by snapshot ID (current by default, or specific if provided)
- All related entities (domains, trails, concepts, sparks, links) share the same snapshot ID for consistency
Endpoints¶
| Endpoint | Purpose |
|---|---|
GET / |
List all snapshots with pagination |
GET /current |
Get current active snapshot |
GET /{id} |
Get snapshot by ID with full details |
GET /¶
Returns all content snapshots with pagination support.
Query Parameters¶
| Parameter | Type | Description |
|---|---|---|
page |
number | Page number (default: 1) |
per_page |
number | Items per page (default: 20, max: 100) |
status |
string | Filter by status: draft, published, or archived |
Response¶
{
"data": [
{
"id": "uuid",
"name": "v2.1.0",
"version_label": "v2.1.0",
"description": "January 2024 content update",
"status": "published",
"entity_counts": {
"domains": 5,
"trails": 18,
"concepts": 65,
"sparks": 280
},
"created_at": "2024-01-15T00:00:00Z",
"published_at": "2024-01-15T10:30:00Z"
}
],
"meta": {
"total": 10,
"page": 1,
"per_page": 20,
"total_pages": 1,
"has_next": false,
"has_prev": false
}
}
Example¶
# List all snapshots
GET /snapshots/
# List only draft snapshots
GET /snapshots/?status=draft
# Paginated list
GET /snapshots/?page=2&per_page=10
GET /current¶
Returns the currently active snapshot that serves as the default for all queries.
Response¶
{
"id": "uuid",
"name": "v2.1.0",
"version_label": "v2.1.0",
"description": "January 2024 content update",
"status": "published",
"entity_counts": {
"domains": 5,
"trails": 18,
"concepts": 65,
"sparks": 280,
"beacons": 45,
"concept_sparks": 520,
"spark_links": 180,
"concept_links": 95
},
"created_at": "2024-01-15T00:00:00Z",
"published_at": "2024-01-15T10:30:00Z",
"parent_snapshot_id": "previous-uuid"
}
Error Response¶
If no snapshot has been published yet:
GET /{id}¶
Get full details of a specific snapshot by ID.
Path Parameters¶
| Parameter | Type | Description |
|---|---|---|
id |
UUID | Snapshot ID |
Response¶
{
"id": "uuid",
"name": "v2.1.0",
"version_label": "v2.1.0",
"description": "January 2024 content update",
"status": "draft",
"entity_counts": {
"domains": 5,
"trails": 18,
"concepts": 65,
"sparks": 280
},
"created_at": "2024-01-15T00:00:00Z",
"published_at": null,
"parent_snapshot_id": "previous-uuid"
}
Using Snapshots in Discovery Queries¶
All Discovery endpoints accept an optional snapshot_id query parameter to query specific snapshot versions.
How Snapshot Selection Works¶
- If
snapshot_idis provided → Uses that specific snapshot - If
snapshot_idis NOT provided → Automatically uses the current active snapshot - If no current snapshot exists → Returns empty results (no snapshot filtering)
Example Queries¶
# Use current snapshot (default behavior)
GET /discovery/domains
# Query specific snapshot
GET /discovery/domains?snapshot_id=abc-123-def-456
# Preview draft snapshot before promoting
GET /discovery/domains?snapshot_id=<draft-snapshot-id>
# Query historical version
GET /discovery/trails?snapshot_id=<old-snapshot-id>
# Combine with other filters
GET /discovery/concepts?trail=neural-networks&snapshot_id=<specific-id>
Supported Discovery Endpoints¶
All discovery endpoints support the snapshot_id parameter:
GET /discovery/domains?snapshot_id={uuid}GET /discovery/trails?snapshot_id={uuid}GET /discovery/concepts?snapshot_id={uuid}GET /discovery/sparks?snapshot_id={uuid}GET /discovery/browse?snapshot_id={uuid}GET /discovery/facets?snapshot_id={uuid}
Complete Workflow Example¶
1. Import Creates Draft Snapshot¶
# Import bundle creates a draft snapshot
deno task import bundle.json
# → Creates snapshot with status="draft"
# → Returns: snapshot_id, import_id
2. Verify Draft Snapshot¶
# Verify what's in the draft snapshot
deno task verify-snapshot:local
# → Shows entity counts, sample entities, relationship verification
3. Preview Draft in API¶
# Preview draft content via API
GET /discovery/domains?snapshot_id=<draft-id>
GET /discovery/browse?snapshot_id=<draft-id>
# → See how draft content looks before promoting
4. Promote Draft to Current¶
# Option A: Via import script
deno task import bundle.json --promote
# Option B: Via SQL (if import already completed)
SELECT promote_import('<import_id>', '<snapshot_id>');
After promotion:
- Draft snapshot becomes status="published"
- Previous current snapshot becomes status="archived"
- New snapshot becomes the current active snapshot
5. Default Queries Use New Snapshot¶
# All queries now automatically use new current snapshot
GET /discovery/domains
GET /discovery/trails
# → No snapshot_id needed, uses current automatically
6. Query Old Snapshot for Comparison¶
# View historical content
GET /discovery/domains?snapshot_id=<old-snapshot-id>
# → Compare versions or view past content
Snapshot Lifecycle¶
┌─────────┐
│ DRAFT │ ← Import creates draft snapshot
└────┬────┘
│
│ promote_import()
▼
┌─────────┐
│ PUBLISHED│ ← Becomes current active snapshot
└────┬────┘
│
│ (new snapshot promoted)
▼
┌──────────┐
│ ARCHIVED │ ← Previous snapshot archived
└──────────┘
Snapshot States¶
draft: Import in progress or awaiting promotion, not visible to default queriespublished: Successfully imported and ready, can be current or archivedarchived: Historical snapshot, replaced by newer published snapshot
Benefits¶
- Atomic Updates: All entities in a snapshot are consistent - either all succeed or all fail
- Zero-Downtime Deployments: Preview drafts before promoting to production
- Rollback Capability: Instantly switch back to previous snapshots
- Historical Queries: Compare versions or view content as it existed at any point
- A/B Testing: Test different content versions side-by-side
- Data Integrity: All related entities (domains, trails, concepts, sparks, links) share the same snapshot ID
Implementation Details¶
Database Schema¶
- All content tables have a
snapshot_idcolumn (UUID, nullable for backward compatibility) - Junction tables (
concept_sparks,spark_links,concept_links) also havesnapshot_id - The
current_snapshottable is a singleton pointing to the active snapshot - Views like
domain_stats,trail_stats,concept_statsrespect snapshot filtering
Query Filtering¶
Every discovery query follows this pattern:
// Get effective snapshot ID
const snapshotId = await getEffectiveSnapshotId(
supabase,
c.req.query("snapshot_id")
);
// Apply snapshot filter
let query = supabase.from("domains").select("*");
if (snapshotId) {
query = query.eq("snapshot_id", snapshotId);
}
This ensures: - Consistent data across all related entities - Historical queries return complete, consistent snapshots - Default queries always use the current published content