Phoenix Gamification
Streaks

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}/streaks

Response

{
  "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}/streaks

Request 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

  • name must be alphanumeric (spaces, hyphens, underscores allowed), max 255 characters.
  • name must be unique within the tenant.
  • config.event_types requires at least one event type.
  • config.window.timezone must be a valid IANA timezone.
  • config.condition.min must be positive.
  • Milestone threshold values 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}/status

Request 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

StatusCodeWhen It Occurs
400VALIDATION_ERRORInvalid JSON, config validation failures, invalid timezone, etc.
401UNAUTHORIZEDMissing or malformed API key headers.
403FORBIDDENAPI key lacks access to the specified tenant.
404NOT_FOUNDRequested streak definition or tenant does not exist.
409CONFLICTDuplicate streak name for the same tenant.
500INTERNAL_ERRORUnexpected 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 paused status 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.