Journeys API — User Progress¶
Edge function:
user· Source:src/routes/me/journeys.ts
Authenticated endpoints for a user's journey enrollments and per-spark progress.
All endpoints require a valid JWT and live inside the user edge function under
the /me/journeys path.
Base URL (local): http://127.0.0.1:54321/functions/v1/user
Authentication: Required — Authorization: Bearer <jwt> on every
request. Cache policy: None — responses are user-specific and mutable.
HTTP tests: http/user.http
Authentication¶
Obtain a JWT via Supabase Auth:
curl -X POST http://127.0.0.1:54321/auth/v1/token?grant_type=password \
-H "apikey: <SUPABASE_ANON_KEY>" \
-H "Content-Type: application/json" \
-d '{"email":"user@example.com","password":"password123"}'
# Response: { "access_token": "...", ... }
Then include in every request:
All endpoints return 401 when the header is missing or the token is expired.
GET /me/journeys — List User's Journeys¶
Return all trail enrollments for the authenticated user, enriched with spark completion counts and trail metadata.
Operation ID: listUserJourneys
Query Parameters¶
| Parameter | Type | Default | Description |
|---|---|---|---|
status |
string | — | Filter by enrollment status: not_started · in_progress · completed |
sort |
string | recent |
Sort order: recent (last activity) · progress (completion %) · started (enroll date) |
Response 200¶
{
"data": [
{
"id": "eeeeeeee-0001-0001-0001-000000000001",
"user_id": "ffffffff-0001-0001-0001-000000000001",
"trail_id": "aaaaaaaa-0001-0001-0001-000000000001",
"status": "in_progress",
"progress_percent": 45,
"started_at": "2026-01-10T00:00:00Z",
"completed_at": null,
"last_activity_at": "2026-02-20T10:30:00Z",
"completed_sparks": 12,
"total_sparks": 27,
"trail": {
"id": "aaaaaaaa-0001-0001-0001-000000000001",
"title": "Agentic AI Mastery",
"slug": "agentic-ai-mastery",
"difficulty_level": "intermediate"
}
}
]
}
Response Schema¶
UserJourneyListItem
| Field | Type | Notes |
|---|---|---|
id |
uuid | Enrollment ID (use for detail endpoint) |
user_id |
uuid | Authenticated user's UUID |
trail_id |
uuid | Journey/trail UUID |
status |
string | not_started · in_progress · completed |
progress_percent |
number | 0–100 |
started_at |
ISO 8601 | Enrollment date |
completed_at |
ISO 8601 | null | Set when status = completed |
last_activity_at |
ISO 8601 | Most recent learning event |
completed_sparks |
integer | Sparks with a completed event |
total_sparks |
integer | Total sparks in the trail |
trail |
TrailSummary |
Journey metadata |
TrailSummary
| Field | Type | Notes |
|---|---|---|
id |
uuid | |
title |
string | |
slug |
string | |
difficulty_level |
string | null |
Response 401¶
GET /me/journeys/{id} — Get Journey Progress Detail¶
Retrieve full progress detail for a specific enrollment — per-spark status list and a "continue here" pointer to the current active concept.
Operation ID: getUserJourney
Path Parameters¶
| Parameter | Type | Description |
|---|---|---|
id |
string (UUID) | Enrollment ID (from list response data[].id) |
Response 200¶
{
"id": "eeeeeeee-0001-0001-0001-000000000001",
"user_id": "ffffffff-0001-0001-0001-000000000001",
"trail_id": "aaaaaaaa-0001-0001-0001-000000000001",
"status": "in_progress",
"progress_percent": 45,
"started_at": "2026-01-10T00:00:00Z",
"completed_at": null,
"last_activity_at": "2026-02-20T10:30:00Z",
"completed_sparks": 12,
"total_sparks": 27,
"current_concept": { // null when fully completed
"id": "cccccccc-0001-0001-0001-000000000003",
"title": "Tool Use Patterns",
"slug": "tool-use-patterns"
},
"spark_progress": [
{
"spark_id": "bbbbbbbb-0001-0001-0001-000000000001",
"title": "What is an AI Agent?",
"status": "completed",
"completed_at": "2026-02-15T09:00:00Z"
},
{
"spark_id": "bbbbbbbb-0001-0001-0001-000000000002",
"title": "Agents vs Chatbots",
"status": "in_progress",
"completed_at": null
},
{
"spark_id": "bbbbbbbb-0001-0001-0001-000000000003",
"title": "ReAct Loop Explained",
"status": "not_started",
"completed_at": null
}
],
"trail": {
"id": "aaaaaaaa-0001-0001-0001-000000000001",
"title": "Agentic AI Mastery",
"slug": "agentic-ai-mastery",
"difficulty_level": "intermediate"
}
}
Response Schema¶
UserJourneyDetail (top-level)
| Field | Type | Notes |
|---|---|---|
id |
uuid | Enrollment ID |
user_id |
uuid | |
trail_id |
uuid | Journey/trail UUID |
status |
string | not_started · in_progress · completed |
progress_percent |
number | 0–100 |
started_at |
ISO 8601 | |
completed_at |
ISO 8601 | null | |
last_activity_at |
ISO 8601 | |
completed_sparks |
integer | |
total_sparks |
integer | |
current_concept |
ConceptSummary | null |
First concept with incomplete sparks; null when done |
spark_progress |
SparkProgress[] |
Per-spark status for all sparks in the trail |
trail |
TrailSummary |
Journey metadata |
ConceptSummary
| Field | Type | Notes |
|---|---|---|
id |
uuid | |
title |
string | |
slug |
string |
SparkProgress
| Field | Type | Notes |
|---|---|---|
spark_id |
uuid | |
title |
string | |
status |
string | See status values below |
completed_at |
ISO 8601 | null | Set when status = completed |
Spark status values:
| Value | Meaning |
|---|---|
not_started |
No learning event recorded for this spark |
in_progress |
started event recorded; no completed event yet |
completed |
completed event recorded |
Response 401¶
Response 404¶
Implementation Notes¶
- The
idpath parameter for the detail endpoint is the enrollment ID, not the trail/journey ID. Use theidfield from the list response. current_conceptpoints to the first concept that still has at least one incomplete spark, giving clients a direct "continue here" pointer. It becomesnullwhen all sparks are completed.- Progress is derived from learning events in the
learning_eventstable — it is not stored as a denormalised column. - These routes are mounted at
/me/journeysviasrc/routes/me/journeys.tsinside theuseredge function (functions/user/index.ts).
See also: Journeys overview · Graph function · Catalog function