Phoenix Gamification
Streaks

Creating Streaks

How to provision, update, and retire streak definitions via the Admin Gateway. Assumes you understand the configuration model from Streak Overview.

Creating 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.


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/streaks?tenant_id=${tenant_id}

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/streaks?tenant_id=${tenant_id}

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/streaks/{definition_id}?tenant_id=${tenant_id}

Returns the full definition record. Use it to confirm deployed configs or seed your admin UI.


5. Update a Streak Definition

PUT /v1/streaks/{definition_id}?tenant_id=${tenant_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/streaks/{definition_id}/status?tenant_id=${tenant_id}

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/streaks/{definition_id}?tenant_id=${tenant_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 Streak Results, which covers how to fetch user streak progress and milestone status.

On this page