Skip to main content

Build a Calendar App: The Golden Path

This tutorial walks you through building ShiftCal, a calendar scheduling app, using every service in The Shift Platform. By the end, you will have registered services in the catalog, set up OAuth, created audit policies, tracked analytics events, defined design tokens, and deployed a live preview -- all through the platform's API.

This tutorial mirrors the golden path E2E test, which exercises every service in sequence.

Prerequisites

  • The Shift Platform running locally (bun run dev) or accessible at a gateway URL
  • An API key (set SHIFT_API_KEY in your environment)

Set your gateway URL:

export GATEWAY_URL=http://localhost:3000
export SHIFT_API_KEY=your-api-key

Phase 0: Health Check

Verify the gateway is up and all services are healthy:

curl $GATEWAY_URL/healthz
# {"status":"ok","service":"shift-gateway"}

curl $GATEWAY_URL/api/v1/health
# Returns health status for all mounted services

Phase 1: Yellow Pages -- Register Services

Register the services that make up ShiftCal. Start by creating an owner and a system, then register the three services.

Create an owner

curl -X POST $GATEWAY_URL/api/v1/catalog/owners \
-H "Content-Type: application/json" \
-H "X-API-Key: $SHIFT_API_KEY" \
-d '{
"name": "ShiftCal Team",
"email": "team@shiftcal.dev",
"team": "product"
}'

Create a system

curl -X POST $GATEWAY_URL/api/v1/catalog/systems \
-H "Content-Type: application/json" \
-H "X-API-Key: $SHIFT_API_KEY" \
-d '{
"name": "google-workspace-integration",
"description": "Google Workspace integration for ShiftCal"
}'

Register services

Register three services: the main ShiftCal app, Google Calendar integration, and Google Gmail integration.

# ShiftCal main app
curl -X POST $GATEWAY_URL/api/v1/catalog/services \
-H "Content-Type: application/json" \
-H "X-API-Key: $SHIFT_API_KEY" \
-d '{
"name": "shiftcal",
"description": "Calendar scheduling application",
"tags": ["calendar", "scheduling", "productivity"],
"system": "google-workspace-integration",
"owner": "ShiftCal Team"
}'

# Google Calendar integration
curl -X POST $GATEWAY_URL/api/v1/catalog/services \
-H "Content-Type: application/json" \
-H "X-API-Key: $SHIFT_API_KEY" \
-d '{
"name": "google-calendar",
"description": "Google Calendar API integration",
"tags": ["google", "calendar", "integration"],
"system": "google-workspace-integration",
"owner": "ShiftCal Team"
}'

# Google Gmail integration
curl -X POST $GATEWAY_URL/api/v1/catalog/services \
-H "Content-Type: application/json" \
-H "X-API-Key: $SHIFT_API_KEY" \
-d '{
"name": "google-gmail",
"description": "Google Gmail API integration for notifications",
"tags": ["google", "email", "notifications"],
"system": "google-workspace-integration",
"owner": "ShiftCal Team"
}'

Add dependencies

ShiftCal depends on both Google integrations:

# shiftcal -> google-calendar
curl -X POST $GATEWAY_URL/api/v1/catalog/services/shiftcal/dependencies \
-H "Content-Type: application/json" \
-H "X-API-Key: $SHIFT_API_KEY" \
-d '{"dependsOn": "<google-calendar-id>"}'

# shiftcal -> google-gmail
curl -X POST $GATEWAY_URL/api/v1/catalog/services/shiftcal/dependencies \
-H "Content-Type: application/json" \
-H "X-API-Key: $SHIFT_API_KEY" \
-d '{"dependsOn": "<google-gmail-id>"}'

Verify with search and dependency graph

# Search for calendar-related services
curl "$GATEWAY_URL/api/v1/catalog/search?q=calendar" \
-H "X-API-Key: $SHIFT_API_KEY"

# Check downstream dependencies
curl "$GATEWAY_URL/api/v1/catalog/deps/shiftcal?direction=down" \
-H "X-API-Key: $SHIFT_API_KEY"

Phase 2: Passport -- Set Up OAuth

Register an OAuth provider and create an authorization for ShiftCal.

Register a provider

curl -X POST $GATEWAY_URL/api/v1/passport/providers \
-H "Content-Type: application/json" \
-H "X-API-Key: $SHIFT_API_KEY" \
-d '{
"name": "google-oauth",
"type": "oauth2",
"clientId": "your-google-client-id",
"clientSecret": "your-google-client-secret",
"authUrl": "https://accounts.google.com/o/oauth2/v2/auth",
"tokenUrl": "https://oauth2.googleapis.com/token",
"scopes": ["openid", "email", "profile", "https://www.googleapis.com/auth/calendar"]
}'

Create an authorization

curl -X POST $GATEWAY_URL/api/v1/passport/authorizations \
-H "Content-Type: application/json" \
-H "X-API-Key: $SHIFT_API_KEY" \
-d '{
"providerId": "<provider-id>",
"serviceId": "<shiftcal-service-id>",
"scopes": ["calendar.read", "calendar.write"],
"grantType": "authorization_code"
}'

Verify audit trail

curl $GATEWAY_URL/api/v1/passport/audit \
-H "X-API-Key: $SHIFT_API_KEY"

Phase 3: Ledger -- Data Governance

Set up retention policies, classify sensitive fields, and emit audit events.

Create a retention policy

curl -X POST $GATEWAY_URL/api/v1/ledger/policies \
-H "Content-Type: application/json" \
-H "X-API-Key: $SHIFT_API_KEY" \
-d '{
"name": "gdpr-90-day",
"service": "shiftcal",
"retentionDays": 90,
"action": "anonymize"
}'

Classify PII fields

# Email is confidential PII
curl -X POST $GATEWAY_URL/api/v1/ledger/classifications \
-H "Content-Type: application/json" \
-H "X-API-Key: $SHIFT_API_KEY" \
-d '{
"service": "shiftcal",
"field": "user.email",
"level": "confidential",
"ppiCategory": "contact-info"
}'

# Booking titles are internal
curl -X POST $GATEWAY_URL/api/v1/ledger/classifications \
-H "Content-Type: application/json" \
-H "X-API-Key: $SHIFT_API_KEY" \
-d '{
"service": "shiftcal",
"field": "booking.title",
"level": "internal"
}'

Emit audit events

curl -X POST $GATEWAY_URL/api/v1/ledger/events \
-H "Content-Type: application/json" \
-H "X-API-Key: $SHIFT_API_KEY" \
-d '{
"action": "booking.created",
"service": "shiftcal",
"resource": "booking-001",
"actor": "user@shiftcal.dev",
"details": {"duration": 60, "attendees": 3}
}'

Phase 4: Pulse -- Track Analytics

Track user events and generate reports.

Track events

curl -X POST $GATEWAY_URL/api/v1/pulse/events \
-H "Content-Type: application/json" \
-H "X-API-Key: $SHIFT_API_KEY" \
-d '{"name": "calendar.viewed", "properties": {"view": "month"}}'

curl -X POST $GATEWAY_URL/api/v1/pulse/events \
-H "Content-Type: application/json" \
-H "X-API-Key: $SHIFT_API_KEY" \
-d '{"name": "booking.created", "properties": {"duration": 30}}'

Batch ingest

curl -X POST $GATEWAY_URL/api/v1/pulse/events/batch \
-H "Content-Type: application/json" \
-H "X-API-Key: $SHIFT_API_KEY" \
-d '{
"events": [
{"name": "page.view", "properties": {"path": "/calendar"}},
{"name": "page.view", "properties": {"path": "/settings"}}
]
}'

Check top events report

curl $GATEWAY_URL/api/v1/pulse/reports/top-events \
-H "X-API-Key: $SHIFT_API_KEY"

Phase 5: Palette -- Design System

Create design tokens and register components.

Create design tokens

curl -X POST $GATEWAY_URL/api/v1/palette/tokens \
-H "Content-Type: application/json" \
-H "X-API-Key: $SHIFT_API_KEY" \
-d '{"name": "color-primary", "value": "#2563eb", "category": "color"}'

curl -X POST $GATEWAY_URL/api/v1/palette/tokens \
-H "Content-Type: application/json" \
-H "X-API-Key: $SHIFT_API_KEY" \
-d '{"name": "color-surface", "value": "#f8fafc", "category": "color"}'

curl -X POST $GATEWAY_URL/api/v1/palette/tokens \
-H "Content-Type: application/json" \
-H "X-API-Key: $SHIFT_API_KEY" \
-d '{"name": "spacing-sm", "value": "0.5rem", "category": "spacing"}'

Register a component

# Create the CalendarGrid component
curl -X POST $GATEWAY_URL/api/v1/palette/components \
-H "Content-Type: application/json" \
-H "X-API-Key: $SHIFT_API_KEY" \
-d '{"name": "CalendarGrid", "description": "Monthly calendar grid with selectable days"}'

# Add props
curl -X POST $GATEWAY_URL/api/v1/palette/components/<component-id>/props \
-H "Content-Type: application/json" \
-H "X-API-Key: $SHIFT_API_KEY" \
-d '{"name": "month", "type": "number", "required": true}'

# Add variant
curl -X POST $GATEWAY_URL/api/v1/palette/components/<component-id>/variants \
-H "Content-Type: application/json" \
-H "X-API-Key: $SHIFT_API_KEY" \
-d '{"name": "compact", "props": {"size": "sm"}}'
# Export as CSS
curl "$GATEWAY_URL/api/v1/palette/export?format=css" \
-H "X-API-Key: $SHIFT_API_KEY"

# Search for calendar-related items
curl "$GATEWAY_URL/api/v1/palette/search?q=calendar" \
-H "X-API-Key: $SHIFT_API_KEY"

Phase 6: Stage -- Deploy a Preview

Create a sandbox session, push application files, and render a live preview.

Create a session

curl -X POST $GATEWAY_URL/api/v1/stage/sessions \
-H "Content-Type: application/json" \
-H "X-API-Key: $SHIFT_API_KEY" \
-d '{"name": "shiftcal-preview"}'

Push application files

curl -X POST $GATEWAY_URL/api/v1/stage/sessions/<session-id>/push \
-H "Content-Type: application/json" \
-H "X-API-Key: $SHIFT_API_KEY" \
-d '{
"files": [
{"path": "App.tsx", "content": "export default function App() { return <h1>ShiftCal</h1>; }"},
{"path": "CalendarGrid.tsx", "content": "export function CalendarGrid() { return <div>Calendar</div>; }"},
{"path": "BookingForm.tsx", "content": "export function BookingForm() { return <form>Booking</form>; }"},
{"path": "AvailabilityPicker.tsx", "content": "export function AvailabilityPicker() { return <div>Pick</div>; }"},
{"path": "styles.css", "content": ".app { padding: 2rem; }"}
]
}'

Render and verify

# Trigger render
curl -X POST $GATEWAY_URL/api/v1/stage/sessions/<session-id>/render \
-H "X-API-Key: $SHIFT_API_KEY"

# Check status
curl $GATEWAY_URL/api/v1/stage/sessions/<session-id>/status \
-H "X-API-Key: $SHIFT_API_KEY"

# Read a file back
curl $GATEWAY_URL/api/v1/stage/sessions/<session-id>/files/App.tsx \
-H "X-API-Key: $SHIFT_API_KEY"

Phase 7: Cross-Service Validation

At this point, all resources exist across all services. Verify everything is connected:

  1. The catalog has 3 services with dependencies
  2. An OAuth provider and authorization exist
  3. Retention policies and classifications are in place
  4. Analytics events have been tracked
  5. Design tokens and components are registered
  6. A Stage session is rendered with all files

Run the full golden path test to validate automatically:

GATEWAY_URL=$GATEWAY_URL bun test release/v0.1.0/golden-path/golden-path.test.ts

Next Steps