---
title: Team Chat dead-letter recovery (re-file failed scheduled messages)
---
# Team Chat dead-letter recovery (re-file failed scheduled messages)

## What it is

**Status:** Server-side Cloud Function is deployed and complete. No UI surface exists yet (in-development, no feature flag).

When a scheduled Team Chat message fails to deliver after exhausting its retry budget (`MAX_DELIVERY_ATTEMPTS`), it is moved to a **dead-letter collection** (`scheduledMessagesDeadLetter`) rather than being silently lost. A production-grade Cloud Function (`teamChatRefileScheduledSend`) can **re-file** a dead-lettered message back into the live scheduled queue with a fresh retry budget.

The server-side recovery path is 100% built. What is missing is a **UI surface** in the renderer that lets users:

1. Discover that a scheduled message was dead-lettered
2. Trigger a re-file (which calls the existing Cloud Function)

## Where to find it

The server side is live with no interface yet; what is planned is an indicator in the scheduled-messages sidebar view.

## How it behaves

### How the re-file works (server side)

The `teamChatRefileScheduledSend` HTTPS relay (Firebase Cloud Function):

1. Authenticates the caller (Firebase ID token)
2. Verifies the caller is the message **author** and an active org/workspace member
3. Reads the dead-letter doc inside a Firestore transaction
4. Validates the doc shape (guards against corrupted data propagation)
5. Atomically **moves** it back to `{channelPath}/scheduledMessages/{id}` with `deliveryAttempts` reset to 0
6. Deletes the dead-letter doc

The move is atomic (set + delete in one transaction), idempotent (same-id overwrites safely), and author-gated (you can only re-file your own messages).

### Security model

Mirrors `teamChatSendMessage`:

- Firebase ID token auth (Authorization: Bearer)
- Author gate: only the message author can re-file
- Membership gate: must still be an active member
- Soft-delete gate: refuses re-file into a deleted workspace
- Disabled-account gate
- Channel path validation (F105 shape check)

### What is needed for UI completion

1. A query/listener on `scheduledMessagesDeadLetter` to detect when dead-lettered messages exist
2. An indicator in the scheduled messages sidebar view (e.g. a badge or a separate section)
3. A "Re-file" or "Retry" action button per dead-lettered message that calls the Cloud Function
4. Error/success feedback (toast or inline status)

The Cloud Function endpoint is ready to call: `POST /t/team-chat-refile-scheduled-send` with body `{ channelPath, deadLetterId }`.

## For agents

### Architecture

- **Dead-letter collection:** `{root}/channels/{channelId}/scheduledMessagesDeadLetter/{id}`
- **Live queue:** `{root}/channels/{channelId}/scheduledMessages/{id}`
- **Cloud Function:** `teamChatRefileScheduledSend` (HTTPS relay, `POST`, bearer auth)
- **Hosting rewrite:** `/t/team-chat-refile-scheduled-send`
- **Source:** `firebase/functions/src/team-chat/scheduled-send-refile.ts`

### Notes for agents

- The dead-letter WRITE side (moving failed messages to the DLQ) is also complete and shipped.
- The server validates the dead-letter doc shape before trusting it (`validateDeadLetterShape`).
- Re-filing resets `deliveryAttempts` to 0, giving the message a fresh retry budget.
- No UI surface or feature flag exists yet for dead-letter visibility or re-filing.
- Related: [team-chat.md](team-chat.md) (scheduled send section), [team-chat-desktop-contract.md](../../.claude/memory/contracts/team-chat-desktop-contract.md).

## Related

Team Chat itself covers the messaging surface the re-file serves; the API platform and export pages are the other planned Team Chat capabilities.
