Create Streaks
This guide teaches you how to provision, update, and retire streak definitions via the Admin Gateway. It assumes you already understand the configuration model from `streak-overview.md`.
1. Authentication Recap
Admin endpoints require API Key authentication. Include either of the following headers:
X-API-Key: <ADMIN_API_KEY>or
Authorization: Bearer <ADMIN_API_KEY>For tenant-scoped routes (/v1/tenants/{tenant_id}/...), the API key must have access to that tenant. Phoenix returns 403 if permissions are insufficient.
2. List Existing Streak Definitions
Endpoint
GET /v1/tenants/{tenant_id}/streaksResponse
{
"definitions": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"tenant_id": "tenant-prod-001",
"name": "Daily Login Streak",
"description": "Track consecutive days users log in",
"status": "active",
"config": { "...": "..." },
"created_at": "2025-01-01T12:00:00Z",
"updated_at": "2025-01-15T09:30:00Z"
}
]
}Use GET /v1/streaks (admin-only) to list streak definitions across all tenants.
3. Create a Streak Definition
Endpoint
POST /v1/tenants/{tenant_id}/streaksRequest Body
{
"name": "Daily Login Streak",
"description": "Track consecutive days users log in",
"config": {
"event_types": ["user.login"],
"window": {
"type": "calendar",
"period": "daily",
"timezone": "UTC",
"reset_time": "00:00"
},
"condition": {
"type": "count",
"min": 1
},
"milestones": [
{ "threshold": 7, "reward_item_id": null, "repeatable": false },
{ "threshold": 30, "reward_item_id": null, "repeatable": false },
{ "threshold": 100, "reward_item_id": null, "repeatable": false }
],
"at_risk_seconds": 3600
}
}Validation Highlights
namemust be alphanumeric (spaces, hyphens, underscores allowed), max 255 characters.namemust be unique within the tenant.config.event_typesrequires at least one event type.config.window.timezonemust be a valid IANA timezone.config.condition.minmust be positive.- Milestone
thresholdvalues must be unique and positive.
Response
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"tenant_id": "tenant-prod-001",
"name": "Daily Login Streak",
"description": "Track consecutive days users log in",
"status": "active",
"config": { "...": "..." },
"created_at": "2025-01-15T10:00:00Z",
"updated_at": "2025-01-15T10:00:00Z"
}cURL Example
curl -X POST \
"$ADMIN_BASE_URL/v1/tenants/$TENANT_ID/streaks" \
-H "Content-Type: application/json" \
-H "X-API-Key: $ADMIN_API_KEY" \
-d '{
"name": "Daily Login Streak",
"description": "Track consecutive days users log in",
"config": {
"event_types": ["user.login"],
"window": {
"type": "calendar",
"period": "daily",
"timezone": "UTC",
"reset_time": "00:00"
},
"condition": {
"type": "count",
"min": 1
},
"milestones": [
{ "threshold": 7, "reward_item_id": null, "repeatable": false }
]
}
}'4. Retrieve a Single Streak Definition
GET /v1/tenants/{tenant_id}/streaks/{definition_id}Returns the full definition record. Use it to confirm deployed configs or seed your admin UI.
5. Update a Streak Definition
PATCH /v1/tenants/{tenant_id}/streaks/{definition_id}Request Body
{
"name": "Updated Streak Name",
"description": "New description text",
"config": {
"event_types": ["user.login"],
"window": {
"type": "calendar",
"period": "daily",
"timezone": "America/New_York",
"reset_time": "04:00"
},
"condition": {
"type": "count",
"min": 1
},
"milestones": [
{ "threshold": 7, "reward_item_id": null, "repeatable": false }
]
}
}All fields are optional. Only provided fields are updated.
Important: Changing config affects how future events are processed. Existing user streaks retain their current counts but will be evaluated against the new configuration going forward.
6. Update Streak Status
PUT /v1/tenants/{tenant_id}/streaks/{definition_id}/statusRequest Body
{
"status": "paused"
}Valid status values:
active– Normal operation; events are processed.paused– Frozen; no window advancement or streak breaks.archived– Soft delete; no processing, data preserved.
Use paused during maintenance or holidays to prevent unfair streak breaks. Switch back to active to resume normal operation.
7. Delete a Streak Definition
DELETE /v1/tenants/{tenant_id}/streaks/{definition_id}Performs a soft delete by setting status to archived. Returns the archived record.
Existing user streak data is preserved for historical reporting. Use deletion sparingly; archived status is usually sufficient to retire a streak while preserving audit history.
8. Configuration Examples
Weekly Workout Streak
{
"name": "Weekly Workout Warrior",
"config": {
"event_types": ["workout.completed"],
"window": {
"type": "calendar",
"period": "weekly",
"timezone": "America/Los_Angeles",
"reset_time": "00:00"
},
"condition": {
"type": "count",
"min": 3
},
"milestones": [
{ "threshold": 4, "reward_item_id": null, "repeatable": false },
{ "threshold": 12, "reward_item_id": null, "repeatable": false }
],
"at_risk_seconds": 86400
}
}Points-Based Daily Challenge
{
"name": "Daily Points Challenge",
"config": {
"event_types": ["game.completed", "challenge.completed"],
"expression": {
"condition": {
"field": "attrs.game_mode",
"operator": "ne",
"value": "tutorial"
}
},
"window": {
"type": "calendar",
"period": "daily",
"timezone": "UTC",
"reset_time": "00:00"
},
"condition": {
"type": "sum",
"field": "attrs.points",
"min": 500
},
"milestones": [
{ "threshold": 7, "reward_item_id": null, "repeatable": false }
]
}
}Rolling 24-Hour Engagement
{
"name": "Stay Active",
"config": {
"event_types": ["*"],
"window": {
"type": "rolling",
"duration_seconds": 86400
},
"condition": {
"type": "count",
"min": 1
},
"milestones": [
{ "threshold": 30, "reward_item_id": null, "repeatable": false }
]
}
}9. Error Reference
| Status | Code | When It Occurs |
|---|---|---|
| 400 | VALIDATION_ERROR | Invalid JSON, config validation failures, invalid timezone, etc. |
| 401 | UNAUTHORIZED | Missing or malformed API key headers. |
| 403 | FORBIDDEN | API key lacks access to the specified tenant. |
| 404 | NOT_FOUND | Requested streak definition or tenant does not exist. |
| 409 | CONFLICT | Duplicate streak name for the same tenant. |
| 500 | INTERNAL_ERROR | Unexpected server issue; retry or contact Phoenix support if it persists. |
Responses follow the standard error envelope:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "config: event_types cannot be empty",
"status": 400
}
}10. Tips Before Going Live
- Use descriptive streak names that players and support teams understand.
- Store configurations in source control to track changes over time.
- Test with short windows (rolling with small
duration_seconds) in staging before production. - Coordinate
pausedstatus with your live ops calendar for holidays or maintenance. - Plan milestones to align with your rewards and notification systems.
Next up: Querying Results, which covers how to fetch user streak progress and milestone status.
Streaks Overview
This guide explains how Phoenix streaks are modeled so you can design engagement mechanics before touching the Admin API. It focuses on the configuration shape you submit when creating a streak definition.
Querying Results
Use the Query Gateway to retrieve user streak progress for profile screens, achievement displays, or analytics dashboards. This guide covers REST endpoints, authentication, and response formats.