Version: 1.0
Last Updated: 2025-11-24
Base URL: https://YOUR-SERVICE-URL.run.app
This document provides comprehensive documentation for the Improv Olympics REST API endpoints.
The Improv Olympics API uses OAuth 2.0 for authentication with Google as the identity provider.
- Initiate Login: Navigate to
/auth/login - Google OAuth: User authenticates with Google
- Callback: Google redirects to
/auth/callbackwith authorization code - Session Cookie: Server sets secure session cookie
- API Access: Subsequent requests include session cookie automatically
For programmatic access (integration tests), you can mock IAP headers:
X-Goog-Authenticated-User-Id: <user-id>
X-Goog-Authenticated-User-Email: <user-email>
Note: In production, these headers are set by Google Identity-Aware Proxy (IAP) and cannot be spoofed.
Access is restricted to users in the allowed users list configured via the ALLOWED_USERS environment variable.
All errors return a JSON object with the following structure:
{
"detail": "Human-readable error message"
}| Code | Meaning | Description |
|---|---|---|
| 200 | OK | Request succeeded |
| 201 | Created | Resource created successfully |
| 400 | Bad Request | Invalid request parameters |
| 401 | Unauthorized | Authentication required |
| 403 | Forbidden | User not authorized for this resource |
| 404 | Not Found | Resource not found |
| 422 | Unprocessable Entity | Validation error |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Server error occurred |
| 503 | Service Unavailable | Service temporarily unavailable |
| 504 | Gateway Timeout | Request timed out |
Rate Limit Exceeded (429):
{
"detail": "Daily session limit of 10 sessions reached. Try again tomorrow."
}Session Not Found (404):
{
"detail": "Session not found or expired"
}Unauthorized Access (403):
{
"detail": "Not authorized to access this session"
}Turn Sequence Error (400):
{
"detail": "Expected turn 3, got 5"
}| Limit Type | Default Value | Scope |
|---|---|---|
| Daily Sessions | 10 | Per user per day |
| Concurrent Sessions | 3 | Per user |
| Request Timeout | 300s | Per request |
Rate limit information is included in API responses:
X-RateLimit-Daily-Limit: 10
X-RateLimit-Daily-Remaining: 7
X-RateLimit-Concurrent-Limit: 3
X-RateLimit-Concurrent-Used: 1
Use GET /api/v1/user/limits to check current rate limit status.
Check if the service is healthy.
Authentication: None required
Request:
curl https://YOUR-SERVICE-URL.run.app/healthResponse (200 OK):
{
"status": "healthy",
"timestamp": "2025-11-24T16:30:00Z"
}Check if the service is ready to accept requests.
Authentication: None required
Request:
curl https://YOUR-SERVICE-URL.run.app/readyResponse (200 OK):
{
"status": "ready",
"timestamp": "2025-11-24T16:30:00Z"
}Initiate OAuth login flow.
Authentication: None required
Request:
curl -L https://YOUR-SERVICE-URL.run.app/auth/loginResponse: Redirect to Google OAuth consent screen
OAuth callback endpoint (handled automatically by browser).
Authentication: None required
Query Parameters:
code(string, required): Authorization code from Googlestate(string, required): CSRF protection token
Response: Redirect to application home with session cookie set
Log out and clear session.
Authentication: Session cookie required
Request:
curl -X GET https://YOUR-SERVICE-URL.run.app/auth/logout \
-b "session_id=<session-cookie>"Response: Redirect to home page with session cleared
Create a new improv session.
Authentication: Required
Rate Limits:
- Daily: 10 sessions per user per day
- Concurrent: 3 active sessions per user
Request Body:
{
"location": "Spaceship Bridge",
"user_name": "Captain Rodriguez"
}Parameters:
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
| location | string | Yes | 1-200 chars | Scene location/setting |
| user_name | string | No | 1-100 chars | Optional display name |
Request Example:
curl -X POST https://YOUR-SERVICE-URL.run.app/api/v1/session/start \
-H "Content-Type: application/json" \
-H "X-Goog-Authenticated-User-Id: user123" \
-H "X-Goog-Authenticated-User-Email: user@example.com" \
-d '{
"location": "Spaceship Bridge",
"user_name": "Captain Rodriguez"
}'Response (201 Created):
{
"session_id": "sess_a1b2c3d4e5f6g7h8",
"status": "initialized",
"location": "Spaceship Bridge",
"created_at": "2025-11-24T16:00:00Z",
"expires_at": "2025-11-24T17:00:00Z",
"turn_count": 0
}Errors:
- 400: Invalid location (empty or too long)
- 401: Not authenticated
- 429: Rate limit exceeded
Retrieve session information.
Authentication: Required (must be session owner)
Path Parameters:
session_id(string, required): Session identifier
Request Example:
curl https://YOUR-SERVICE-URL.run.app/api/v1/session/sess_a1b2c3d4e5f6g7h8 \
-H "X-Goog-Authenticated-User-Id: user123" \
-H "X-Goog-Authenticated-User-Email: user@example.com"Response (200 OK):
{
"session_id": "sess_a1b2c3d4e5f6g7h8",
"status": "active",
"location": "Spaceship Bridge",
"created_at": "2025-11-24T16:00:00Z",
"expires_at": "2025-11-24T17:00:00Z",
"turn_count": 5
}Errors:
- 401: Not authenticated
- 403: Not authorized (not session owner)
- 404: Session not found or expired
Execute a turn in the improv session.
Authentication: Required (must be session owner)
Path Parameters:
session_id(string, required): Session identifier
Request Body:
{
"user_input": "Captain's log, stardate 2345.6. We've entered orbit around Mars.",
"turn_number": 1
}Parameters:
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
| user_input | string | Yes | 1-1000 chars | User's scene contribution |
| turn_number | integer | Yes | >= 1 | Turn number (must be sequential) |
Request Example:
curl -X POST https://YOUR-SERVICE-URL.run.app/api/v1/session/sess_a1b2c3d4e5f6g7h8/turn \
-H "Content-Type: application/json" \
-H "X-Goog-Authenticated-User-Id: user123" \
-H "X-Goog-Authenticated-User-Email: user@example.com" \
-d '{
"user_input": "Captain'\''s log, stardate 2345.6. We'\''ve entered orbit around Mars.",
"turn_number": 1
}'Response (200 OK):
{
"turn_number": 1,
"partner_response": "Acknowledged, Captain. All systems nominal. Shall I initiate landing sequence?",
"room_vibe": {
"analysis": "The audience is engaged and curious about the mission.",
"energy": "positive",
"engagement_level": "high"
},
"current_phase": 1,
"timestamp": "2025-11-24T16:05:00Z",
"coach_feedback": null
}Response Fields:
| Field | Type | Description |
|---|---|---|
| turn_number | integer | Turn number executed |
| partner_response | string | Partner agent's scene contribution |
| room_vibe | object | Audience sentiment analysis |
| room_vibe.analysis | string | Qualitative audience analysis |
| room_vibe.energy | string | Audience energy level |
| room_vibe.engagement_level | string | Audience engagement rating |
| current_phase | integer | Current partner phase (1 or 2) |
| timestamp | string (ISO 8601) | Turn completion timestamp |
| coach_feedback | string | null | Coach feedback (only at turn 15+) |
Phase Transitions:
- Phase 1 (Turns 1-4): Partner is supportive and helpful
- Phase 2 (Turns 5+): Partner becomes more fallible and human
Coach Feedback:
- Appears starting at turn 15
- Provides constructive feedback on improv techniques
- Highlights strengths and areas for improvement
Errors:
- 400: Invalid turn number (out of sequence)
- 401: Not authenticated
- 403: Not authorized (not session owner)
- 404: Session not found
- 422: Content policy violation (offensive content or prompt injection)
- 504: Agent execution timeout (>30s)
Close an active session.
Authentication: Required (must be session owner)
Path Parameters:
session_id(string, required): Session identifier
Request Example:
curl -X POST https://YOUR-SERVICE-URL.run.app/api/v1/session/sess_a1b2c3d4e5f6g7h8/close \
-H "X-Goog-Authenticated-User-Id: user123" \
-H "X-Goog-Authenticated-User-Email: user@example.com"Response (200 OK):
{
"status": "closed",
"session_id": "sess_a1b2c3d4e5f6g7h8"
}Errors:
- 401: Not authenticated
- 403: Not authorized (not session owner)
- 404: Session not found
Get current rate limit status for authenticated user.
Authentication: Required
Request Example:
curl https://YOUR-SERVICE-URL.run.app/api/v1/user/limits \
-H "X-Goog-Authenticated-User-Id: user123" \
-H "X-Goog-Authenticated-User-Email: user@example.com"Response (200 OK):
{
"user_id": "user123",
"limits": {
"daily_sessions_limit": 10,
"daily_sessions_used": 3,
"daily_sessions_remaining": 7,
"concurrent_sessions_limit": 3,
"concurrent_sessions_count": 1,
"concurrent_sessions_remaining": 2,
"daily_reset_at": "2025-11-25T00:00:00Z"
}
}Response Fields:
| Field | Type | Description |
|---|---|---|
| user_id | string | Authenticated user ID |
| daily_sessions_limit | integer | Max sessions per day |
| daily_sessions_used | integer | Sessions used today |
| daily_sessions_remaining | integer | Sessions remaining today |
| concurrent_sessions_limit | integer | Max concurrent sessions |
| concurrent_sessions_count | integer | Current active sessions |
| concurrent_sessions_remaining | integer | Concurrent slots available |
| daily_reset_at | string (ISO 8601) | When daily counter resets |
Errors:
- 401: Not authenticated
{
"location": "string (1-200 chars, required)",
"user_name": "string (1-100 chars, optional)"
}{
"session_id": "string",
"status": "initialized|active|closed|timeout",
"location": "string",
"created_at": "ISO 8601 datetime",
"expires_at": "ISO 8601 datetime",
"turn_count": "integer"
}{
"user_input": "string (1-1000 chars, required)",
"turn_number": "integer (>= 1, required)"
}{
"turn_number": "integer",
"partner_response": "string",
"room_vibe": {
"analysis": "string",
"energy": "string",
"engagement_level": "string"
},
"current_phase": "integer (1 or 2)",
"timestamp": "ISO 8601 datetime",
"coach_feedback": "string | null"
}# Step 1: Start session
SESSION_RESPONSE=$(curl -s -X POST https://YOUR-SERVICE-URL.run.app/api/v1/session/start \
-H "Content-Type: application/json" \
-H "X-Goog-Authenticated-User-Id: user123" \
-H "X-Goog-Authenticated-User-Email: user@example.com" \
-d '{"location": "Mars Research Station"}')
SESSION_ID=$(echo $SESSION_RESPONSE | jq -r '.session_id')
echo "Session created: $SESSION_ID"
# Step 2: Execute turns 1-15
for i in {1..15}; do
echo "Executing turn $i..."
TURN_RESPONSE=$(curl -s -X POST \
https://YOUR-SERVICE-URL.run.app/api/v1/session/$SESSION_ID/turn \
-H "Content-Type: application/json" \
-H "X-Goog-Authenticated-User-Id: user123" \
-H "X-Goog-Authenticated-User-Email: user@example.com" \
-d "{
\"user_input\": \"This is my improvisation for turn $i\",
\"turn_number\": $i
}")
PARTNER_RESPONSE=$(echo $TURN_RESPONSE | jq -r '.partner_response')
echo "Partner: $PARTNER_RESPONSE"
# Check for coach feedback at turn 15
if [ $i -eq 15 ]; then
COACH_FEEDBACK=$(echo $TURN_RESPONSE | jq -r '.coach_feedback')
echo "Coach: $COACH_FEEDBACK"
fi
sleep 1
done
# Step 3: Close session
curl -X POST https://YOUR-SERVICE-URL.run.app/api/v1/session/$SESSION_ID/close \
-H "X-Goog-Authenticated-User-Id: user123" \
-H "X-Goog-Authenticated-User-Email: user@example.com"
echo "Session completed!"curl https://YOUR-SERVICE-URL.run.app/api/v1/user/limits \
-H "X-Goog-Authenticated-User-Id: user123" \
-H "X-Goog-Authenticated-User-Email: user@example.com" \
| jq '.'curl https://YOUR-SERVICE-URL.run.app/api/v1/session/sess_a1b2c3d4e5f6g7h8 \
-H "X-Goog-Authenticated-User-Id: user123" \
-H "X-Goog-Authenticated-User-Email: user@example.com" \
| jq '.'Run the automated smoke test suite:
python scripts/smoke_test.py --url https://YOUR-SERVICE-URL.run.appUse tools like Postman, Insomnia, or curl to manually test endpoints.
Postman Collection: Available at docs/postman_collection.json (if created)
Integration tests require real infrastructure:
pytest tests/integration/ --run-integration- initialized: Session created, no turns executed
- active: At least one turn executed
- closed: Manually closed by user
- timeout: Expired after 60 minutes of inactivity
- Content Filtering: Offensive content is blocked (HTTP 400)
- PII Detection: Personal information is redacted from logs
- Prompt Injection Protection: Injection attempts are blocked (HTTP 422)
- Rate Limiting: Prevents abuse via daily and concurrent limits
- Access Control: Only allowed users can access the API
- Turn Execution: Typically 5-8 seconds (ADK agent execution)
- Timeout: 30 seconds per turn
- Session Timeout: 60 minutes of inactivity
- Request Timeout: 300 seconds maximum
- Handle Errors Gracefully: Always check HTTP status codes
- Respect Rate Limits: Check
/user/limitsbefore creating sessions - Sequential Turns: Always send turns in sequence (1, 2, 3...)
- Close Sessions: Call
/closewhen done to free concurrent slots - Timeout Handling: Be prepared for 504 timeouts on slow ADK execution
Document Version: 1.0.0 Last Updated: 2025-11-24 Contact: See DEPLOYMENT_RUNBOOK.md for support contacts