How It Works
The complete lifecycle of a Profer artifact
How It Works
The lifecycle
Agent in Claude Code / Cursor / any MCP client
│
├── 1. publish({ html, title, questions })
│ └── Profer stores HTML + questions
│ └── Returns { id: "PF-K8M3N", url }
│
│ 2. User shares URL (Slack, email, WhatsApp)
│ │
│ ├── Reviewer opens URL in browser
│ │ ├── Sees agent's HTML (sandboxed iframe)
│ │ ├── Answers structured questions
│ │ └── Submits (no login required)
│ │
│ └── Webhook fires (if configured)
│
│ 3. Feedback aggregated in database
│
└── 4. get({ id: "PF-K8M3N" })
└── Returns { html, feedback[], status }Key design decision: the agent generates the HTML
Profer doesn't have templates, themes, or renderers. The agent generates whatever HTML it wants. We just:
- Store it — in Supabase Postgres
- Serve it — at a public URL
- Wrap it — with a feedback widget
- Return feedback — structured, typed data
This means any agent can use Profer. Claude generates different HTML than GPT. Both work.
Public page rendering
When a reviewer opens profer.dev/v/PF-K8M3N:
- Edge function reads the page from Postgres
- Agent HTML is placed inside a sandboxed iframe (
srcdocwithsandbox="allow-same-origin") - CSP headers with a per-request nonce protect against XSS
- The feedback widget renders below the iframe
- Submitting feedback POSTs to
/v/PF-K8M3N/feedback
Status tracking
Pages have three statuses:
| Status | Meaning |
|---|---|
awaiting_feedback | Initial state, or after an update() |
approved | All approve-type questions answered "yes" |
changes_requested | Any approve-type question answered "no" or "needs_changes" |
Status auto-updates when feedback with approve-type questions is submitted.
Version tracking
Each update() increments the version number and resets status to awaiting_feedback. Previous feedback is preserved and tied to the version it was given on.