Appearance
Recipe: Custom site backend
End-to-end pattern for a custom-built marketing or client site.
Architecture
text
Browser (React/Vue/etc.)
│ POST /api/appraisal-request (your auth / CAPTCHA)
▼
Your server (holds AF_API_KEY)
│ Public API
▼
Appraiser Flow Open Quote queue
│ webhook quote.created
▼
Your CRM / Slack / email notifierSteps
- Create an
af_test_key withproducts:read,fees:read,quotes:write,quotes:read,files:write. - On form focus/blur of address fields, call your backend →
POST /public/v1/coverage/checkand/fees/preview. - On submit, your backend:
- Validates input
- Generates
Idempotency-Keyfrom your submission id POST /public/v1/quotes- Optionally uploads LOE via multipart
- Show the user a thank-you page with your reference number (
external_reference). - Register a webhook; on
quote.created/quote.sent, update your CRM. - Staff completes the quote in Appraiser Flow; client receives fee + turnaround only.
Minimal Express handler (sketch)
js
app.post("/api/appraisal-request", async (req, res) => {
const idempotencyKey = req.body.submissionId; // stable
const r = await fetch(`${process.env.AF_API}/public/v1/quotes`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.AF_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": idempotencyKey,
},
body: JSON.stringify({
schema_version: 1,
external_reference: idempotencyKey,
product_code: req.body.productCode,
subject: req.body.subject,
client: req.body.client,
source: "website",
}),
});
const data = await r.json();
if (!r.ok) return res.status(r.status).json(data);
return res.json({ ok: true, quoteId: data.id });
});Checklist
- [ ] API key only on server
- [ ] CAPTCHA or auth on public form
- [ ] Idempotency key per submission
- [ ] Fee UI labeled as fee/turnaround, not value
- [ ] Webhook signature verified
- [ ] Staging tested with
af_test_beforeaf_live_