> ## Documentation Index
> Fetch the complete documentation index at: https://docs.encoreos.io/llms.txt
> Use this file to discover all available pages before exploring further.

# AI Staff Headshot Generator — Integration Document

> Status: ✅ Complete Spec: specs/pf/specs/PF-98-ai-staff-headshot-generator.md Tasks: specs/pf/tasks/PF-98-TASKS.md Context: specs/pf/specs/PF-98-CONTEXT.md Crea…

**Status:** ✅ Complete\
**Spec:** [`specs/pf/specs/PF-98-ai-staff-headshot-generator.md`](../../../specs/pf/specs/PF-98-ai-staff-headshot-generator.md)\
**Tasks:** [`specs/pf/tasks/PF-98-TASKS.md`](../../../specs/pf/tasks/PF-98-TASKS.md)\
**Context:** [`specs/pf/specs/PF-98-CONTEXT.md`](../../../specs/pf/specs/PF-98-CONTEXT.md)\
**Created:** 2026-04-11\
**Updated:** 2026-04-11

***

## Purpose

Defines integration contracts for the AI Staff Headshot Generator feature: events published, storage contracts, provider API abstraction, and Edge Function inventory. PF-98 is a PF-internal feature with no cross-core consumers; integrations are limited to PF platform services (notifications, quota tracking).

***

## Dependencies

| Dependency | Purpose                                                          | Integration Type |
| ---------- | ---------------------------------------------------------------- | ---------------- |
| PF-04      | Audit logging for consent, generation, deletion events           | Platform Layer   |
| PF-06      | Permission keys for headshot and campaign operations             | Platform Layer   |
| PF-10      | Notifications (campaign invites, generation complete, reminders) | Event            |
| PF-27      | Profile photo update (set generated headshot as profile photo)   | Platform Layer   |
| PF-30      | Organization settings (provider config, quotas, retention)       | Platform Layer   |
| PF-43      | Quota tracking (generation credits per org)                      | Event            |
| PF-56      | Biometric consent management (upload consent gate)               | Platform Layer   |
| PF-66      | File upload patterns (react-dropzone, validation)                | Platform Layer   |

***

## Events Published

### `pf_headshot_job_completed`

* **Publisher:** PF-98 (Edge Function `pf-headshot-webhook`)
* **Subscribers:** PF-10 (user notification), PF-43 (quota decrement)
* **Trigger:** AI provider webhook confirms job completion
* **Payload:** See `EVENT_CONTRACTS.md` § PF-98

### `pf_headshot_campaign_completed`

* **Publisher:** PF-98 (Edge Function `pf-headshot-webhook` or `pf-headshot-status-poll`)
* **Subscribers:** PF-10 (admin notification)
* **Trigger:** All campaign members have completed headshot generation
* **Payload:** See `EVENT_CONTRACTS.md` § PF-98

***

## Storage Contract

| Property       | Value                                                                                                        |
| -------------- | ------------------------------------------------------------------------------------------------------------ |
| Bucket         | `headshots` (private)                                                                                        |
| Upload path    | `{org_id}/{user_id}/uploads/{upload_id}/{filename}`                                                          |
| Generated path | `{org_id}/{user_id}/{job_id}/{filename}`                                                                     |
| Access         | Signed URLs only (1-hour expiry)                                                                             |
| Retention      | Source photos: configurable (`upload_retention_days`, default 90); Generated: indefinite unless user-deleted |
| Cleanup        | Daily cron via `pf-headshot-cleanup` Edge Function                                                           |

***

## Edge Functions

| Function                      | Purpose                                                             |
| ----------------------------- | ------------------------------------------------------------------- |
| `pf-headshot-submit`          | Receives upload metadata + style; dispatches to configured provider |
| `pf-headshot-webhook`         | Receives provider callbacks; updates job status; publishes events   |
| `pf-headshot-status-poll`     | Polls provider status for providers without webhook support         |
| `pf-headshot-signed-url`      | Generates signed URLs for gallery display                           |
| `pf-headshot-cleanup`         | Daily cron: deletes expired source photos per retention config      |
| `pf-headshot-campaign-invite` | Sends batch PF-10 notifications for campaign invites                |

***

## Permission Keys

| Key                        | Description                                   |
| -------------------------- | --------------------------------------------- |
| `headshot.upload.create`   | Upload source photos                          |
| `headshot.job.create`      | Trigger headshot generation                   |
| `headshot.gallery.read`    | View own generated headshots                  |
| `headshot.gallery.delete`  | Delete own headshots                          |
| `headshot.campaign.manage` | Create/edit/delete campaigns (admin)          |
| `headshot.settings.manage` | Configure provider, quotas, retention (admin) |

***

## Notes

* PF-98 is PF-internal; no other core consumes or publishes to it.
* Provider API keys are stored as Supabase Edge Function secrets, not in the database.
* RLS helpers must use `expires_at` check on `pf_user_role_assignments` (not `is_active`); see `PF-98-CONTEXT.md` § Area 7.
* Trigger functions use `update_updated_at_column()` (not `pf_set_updated_at()`).
