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_KEYin 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 and search
# 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:
- The catalog has 3 services with dependencies
- An OAuth provider and authorization exist
- Retention policies and classifications are in place
- Analytics events have been tracked
- Design tokens and components are registered
- 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
- Read the SDK documentation to use the TypeScript client instead of raw HTTP
- Explore the CLI reference for command-line access to all services
- Check the Agent Integration guide for programmatic workflows