# Recently Removed Events API Spec **For:** Home Dashboard "Recently Removed" widget **Status:** Backend ready โ€” awaiting frontend implementation **Owner:** Socrates (backend) / Daedalus (frontend) **Related Spec:** See Daedalus' frontend implementation spec at `workspace-daedalus/dashboard/REMOVED-WIDGET-SPEC.md` --- ## Auth For production, use **session-based auth** or **proxy pattern** โ€” never expose the secret in browser HTML. See "Security Notes" below. --- ## Endpoint ``` GET /family/events/removed ``` **Auth:** `X-Hoffdesk-Secret` header (same as webhook) ## Query Parameters | Param | Type | Default | Description | |-------|------|---------|-------------| | `hours` | int | 24 | How far back to look | | `limit` | int | 20 | Max results to return | ## Response ```json { "removals": [ { "uid": "dceb54d1-2cc4-4d1c-9210-775a1c0c8442", "summary": "Softball Tryouts", "start_time": "2026-04-28T15:30:00", "removed_at": "2026-04-24T01:35:22.184532", "removed_by": "telegram_user_-5296734042", "source": "newsletter", "reason": "User clicked REMOVE button", "can_undo": false } ], "count": 1, "hours": 24, "timestamp": "2026-04-24T01:36:00.000000" } ``` ## Fields | Field | Type | Description | |-------|------|-------------| | `uid` | string | Event unique ID (Radicale UID) | | `summary` | string | Event title | | `start_time` | ISO 8601 | When event was scheduled | | `removed_at` | ISO 8601 | When it was deleted | | `removed_by` | string | Who removed it (telegram_user_{chat_id}) | | `source` | string | Where it came from: `newsletter`, `appointment`, `family` | | `reason` | string | Why it was removed (optional) | | `can_undo` | boolean | Always `false` โ€” Radicale doesn't support undo | ## UI Suggestions **Widget Design:** - Compact list (max 5 items visible, scroll for more) - Show: `summary` + `start_time` (formatted nicely) - Subtle timestamp: "Removed 2 hours ago" - Group by source with icons: ๐Ÿ“ฐ newsletter, ๐Ÿ“ง appointment, etc. **Example Layout:** ``` โ”Œโ”€ Recently Removed โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๐Ÿ“ฐ Softball Tryups โ”‚ โ”‚ Apr 28, 3:30 PM ยท 2h ago โ”‚ โ”‚ ๐Ÿ“ฐ Spirit Week โ”‚ โ”‚ Apr 28-30 ยท 5h ago โ”‚ โ”‚ โ”‚ โ”‚ [View All โ†’] โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ``` **Empty State:** - Hide widget entirely if `count === 0` - Or show: "No events removed recently โ€” all good!" ## Integration Notes - Poll every 30-60 seconds if user is on dashboard - Or refresh on dashboard load - No real-time WebSocket (not needed for this) ## Security Notes **โš ๏ธ Never expose `X-Hoffdesk-Secret` in browser HTML or JavaScript.** **Recommended patterns:** | Pattern | How | Security | |---------|-----|----------| | **Session cookie** (best) | User logs in โ†’ session cookie โ†’ server validates | Secret stays server-side | | **Proxy** (good for dev) | Dashboard server adds header when proxying to API | Secret in env, not browser | | **Meta tag** (dev only) | `` JS reads | โŒ Visible in View Source | **For production with Aundrea:** Use session cookies or proxy pattern. She should never see API secrets. --- ## Backend Behavior - Events are logged when user clicks REMOVE button in Telegram - Retained for 30 days then auto-purged - Logs stored at: `hoffdesk-api/data/removed_events.jsonl`