# Presence Activity API Documentation ## Overview The Presence Activity API allows users to manage their current activities (e.g., gaming, music, workouts) with automatic expiration through a lease-based system. Activities can be created, updated, and deleted, with support for flexible metadata and both system-generated and user-defined identifiers. This service is handled by the DysonNetwork.Pass, when using with the gateway, replace the `/api` with the `/pass` ## Key Features - **Lease-Based Expiration**: Activities automatically expire within 1-60 minutes unless renewed - **Flexible Identity**: Support for both autogenerated GUIDs and user-defined ManualIds - **Extensible Metadata**: JSON-stored metadata dictionary for custom developer data - **Soft Deletion**: Activities are soft-deleted and filtered automatically - **Performance Optimized**: Cached active activities with 1-minute expiration - **Authentication Required**: All endpoints require valid user authentication ## API Endpoints ### Base URL: `/api/activities` ### Authentication All endpoints require `[Authorize]` header. User context is automatically extracted. --- ## Get Active Activities Retrieve all currently active (non-expired) presence activities for the authenticated user. **Endpoint:** `GET /api/activities` **Response:** ```json [ { "id": "550e8400-e29b-41d4-a716-446655440000", "type": "Gaming", "manualId": "game-session-1", "title": "Playing Cyberpunk 2077", "subtitle": "Night City Exploration", "caption": "Missions completed: 15", "meta": { "appName": "Cyberpunk 2077", "platform": "Steam", "customProperty": "additional data" }, "leaseMinutes": 10, "leaseExpiresAt": "2024-01-15T14:30:00Z", "accountId": "user-guid", "createdAt": "2024-01-15T14:25:00Z", "updatedAt": "2024-01-15T14:25:00Z", "deletedAt": null } ] ``` **Common Response Codes:** - `200 OK` - Success, returns array of active activities - `401 Unauthorized` - Invalid or missing authentication --- ## Create New Activity Create a new presence activity with a configurable lease period. **Endpoint:** `POST /api/activities` **Request Body:** ```json { "type": "Gaming", "manualId": "my-game-session", "title": "Playing Cyberpunk 2077", "subtitle": "Night City Mission", "caption": "Currently exploring downtown", "meta": { "appName": "Cyberpunk 2077", "platform": "Steam", "difficulty": "Hard", "mods": ["mod1", "mod2"] }, "leaseMinutes": 15 } ``` **Response:** Returns the created `SnPresenceActivity` object with populated fields. **Field Details:** - `type`: PresenceType enum (Unknown, Gaming, Music, Workout) - `manualId`: Optional user-defined string identifier - `title`, `subtitle`, `caption`: Display strings (max 4096 chars each) - `meta`: Optional `Dictionary` for custom data - `leaseMinutes`: 1-60 minutes (default: 5) **Response Codes:** - `200 OK` - Activity created successfully - `400 Bad Request` - Invalid lease minutes or malformed data - `401 Unauthorized` - Invalid authentication --- ## Update Activity Update an existing activity using either its GUID or ManualId. Only provided fields are updated. **Endpoint:** `PUT /api/activities` **Query Parameters:** (one required) - `id` - System-generated GUID (string) - `manualId` - User-defined identifier (string) **Request Body:** (all fields optional) ```json { "title": "Updated: Playing Cyberpunk 2077", "meta": { "appName": "Cyberpunk 2077", "platform": "Steam", "newProperty": "updated data" }, "leaseMinutes": 20 } ``` **Response:** Returns the updated `SnPresenceActivity` object. **Response Codes:** - `200 OK` - Activity updated successfully - `400 Bad Request` - Missing or invalid ID parameters - `401 Unauthorized` - Invalid authentication - `404 Not Found` - Activity not found or doesn't belong to user **Example cURL:** ```bash # Update by ManualId curl -X PUT "/api/activities?manualId=my-game-session" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"leaseMinutes": 20}' # Update by GUID curl -X PUT "/api/activities?id=550e8400-e29b-41d4-a716-446655440000" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"title": "Updated Title"}' ``` --- ## Delete Activity Soft-delete an activity using either GUID or ManualId. **Endpoint:** `DELETE /api/activities` **Query Parameters:** (one required) - `id` - System-generated GUID (string) - `manualId` - User-defined identifier (string) **Request Body:** None **Response:** No content (204) **Response Codes:** - `204 No Content` - Activity deleted successfully - `400 Bad Request` - Missing or invalid ID parameters - `401 Unauthorized` - Invalid authentication - `404 Not Found` - Activity not found or doesn't belong to user **Example cURL:** ```bash # Delete by ManualId curl -X DELETE "/api/activities?manualId=my-game-session" \ -H "Authorization: Bearer " ``` --- ## Additional Endpoint ### Get Activities by Account ID **Endpoint:** `GET /api/activities/{accountId:guid}` For administrative or debugging purposes. Returns activities for the specified account ID, regardless of authentication. --- ## Data Models ### PresenceType Enum ```csharp public enum PresenceType { Unknown, Gaming, Music, Workout } ``` ### SnPresenceActivity ```csharp public class SnPresenceActivity : ModelBase { public Guid Id { get; set; } // System-generated GUID public PresenceType Type { get; set; } public string? ManualId { get; set; } // User-defined ID public string? Title { get; set; } public string? Subtitle { get; set; } public string? Caption { get; set; } public Dictionary? Meta { get; set; } // JSON metadata public int LeaseMinutes { get; set; } // Lease duration public Instant LeaseExpiresAt { get; set; } // Expiration timestamp // Inherited from ModelBase public Guid AccountId { get; set; } public Instant CreatedAt { get; set; } public Instant UpdatedAt { get; set; } public Instant? DeletedAt { get; set; } // Soft deletion } ``` ## Behavior & Constraints ### Lease Expiration - Activities automatically expire when `SystemClock.Instance.GetCurrentInstant() > LeaseExpiresAt` - Expiry is checked in database queries, so expired activities are filtered out of GET operations - Clients must periodically update/renew leases to keep activities active ### ID Flexibility - **ManualId**: User-defined string, unique within a user's activities - **GUID**: System-generated, always unique, returned in API responses - Both can be used interchangeably for updates and deletion ### Performance Optimizations - Active activities are cached for 1 minute to handle frequent updates - Cache is invalidated on create/update/delete operations - Database queries filter expired activities automatically ### Security - All operations are scoped to the authenticated user's account - Users can only manage their own activities - Invalid or expired authentication tokens return 401 Unauthorized ### Data Storage - Activities are stored in PostgreSQL with JSONB metadata support - Soft deletion uses timestamp rather than hard removal - EF Core middleware automatically handles CreatedAt/UpdatedAt timestamps ## Usage Examples ### Gaming Session Management ```javascript // Start gaming session const activity = await fetch('/api/activities', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ type: 'Gaming', manualId: 'game-session-1', title: 'Playing Cyberpunk 2077', meta: { appId: 'cyberpunk2077', mods: ['photorealistic'] }, leaseMinutes: 15 }) }); // Update progress (extend lease) await fetch('/api/activities?manualId=game-session-1', { method: 'PUT', body: JSON.stringify({ title: 'Playing Cyberpunk 2077 - Level 25', leaseMinutes: 15 }) }); // End session await fetch('/api/activities?manualId=game-session-1', { method: 'DELETE' }); ``` ### Metadata Extension ```javascript // Rich metadata support const activity = { type: 'Music', manualId: 'spotify-session', title: 'Listening to Electronic', meta: { spotifyTrackId: '1Je1IMUlBXcx1FzbcXRuWw', artist: 'Purity Ring', album: 'Shrines', duration: 240000, // milliseconds custom: { userRating: 5, genre: 'Electronic' } }, leaseMinutes: 30 }; ``` ## Error Handling Common error responses follow REST API conventions: ```json { "type": "Microsoft.AspNetCore.Mvc.ValidationProblemDetails", "title": "One or more validation errors occurred.", "status": 400, "errors": { "leaseMinutes": ["Lease minutes must be between 1 and 60."] } } ``` ## Implementation Notes - Built with ASP.NET Core and Entity Framework Core - Uses NodaTime for precise timestamp handling - PostgreSQL JSONB for flexible metadata storage - Integration with existing authentication and caching systems - Follows established project patterns for soft deletion and audit trails