# Files And Retention

Hooksbase stores file uploads from form and email ingest as separate file objects so downstream consumers can download them without re-reading the original raw message body. Files are delivery-adjacent, not a standalone document store.

## File model

Files are uploaded when the project's `fileStorageBytes` quota is greater than zero. In practice that means:

- form and email ingest can attach file objects to a delivery
- each stored file is tracked separately from the delivery row
- payload JSON includes `fileRef: { key, url }` entries when a file object exists
- free-tier projects keep metadata only and never upload the file content

Limits today:

- per-file limit: `15 MB`
- per-ingest total: `50 MB`

## Auth model

- **Signed download URL**: `GET /v1/files/{signedToken}` is public. The signed token carries authority and does not require a project API key.
- **Public API** `project API key`: `GET /v1/webhooks/{webhookId}/deliveries/{deliveryId}/files/{index}` is the durable authenticated download route.
- **Dashboard**: Delivery body previews refresh stale `fileRef.url` values so the UI can keep downloading attachments after the original signed URL expires.

## Signed URLs and authenticated download

Signed URLs are convenience links, not permanent identifiers:

- `fileRef.url` is HMAC-signed and valid for `7 days` from issuance
- the public signed route returns `404` when the token is invalid, expired, or the underlying object is gone
- the authenticated delivery-file route (under `/v1/webhooks/{webhookId}/deliveries/{deliveryId}/files/{index}`) stays available as long as the file object still exists and the project API key has access to the webhook

Use the signed URL for short-lived app flows (email recipients, browser previews) and the authenticated route for durable operator or backend tooling. The dashboard automatically re-issues fresh signed URLs when it renders a delivery, so stale `fileRef.url` values in persisted JSON do not need to block the UI.

**Download from a signed URL**

```bash
curl "https://api.hooksbase.com/v1/files/file_token_..."
```

**Download with a project key**

```bash
curl https://api.hooksbase.com/v1/webhooks/wh_123/deliveries/del_123/files/0 \
  -H "Authorization: Bearer swk_..." \
  --output attachment.bin
```

## Retention and replay behavior

Files follow the same retention mindset as payload objects:

- stored file bytes count against project and webhook file-storage quota
- maintenance prunes expired file objects using the effective payload retention window
- deletion is blocked while unre-driven DLQ rows still depend on the source delivery
- replay deliveries share the source file objects instead of re-uploading them

This is why files are documented here with retention rather than as a generic media-management feature. They exist to support delivery workflows.

## Related routes

- `GET /v1/files/{signedToken}`
- `GET /v1/webhooks/{webhookId}/deliveries/{deliveryId}/files/{index}`
- `GET /v1/deliveries/{id}`
- `GET /v1/dlq/{id}`

## Common mistakes

- Treating `fileRef.url` as a permanent stable URL.
- Assuming free-tier projects persist file content instead of metadata only.
- Expecting replay to mint brand-new file objects instead of reusing the original ones.
- Using Hooksbase file storage as a general-purpose asset bucket outside the delivery lifecycle.
