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​