One funnel for every fabrication process: quote → refine → checkout → track.
Live processes: fdm_print and resin_print (3D printing),
cnc, sheetmetal, laser_cut,
decal (custom stickers), print (paper — cards, flyers,
posters), and apparel, US shipping. AFN speaks UFP — the Universal Fabrication Protocol.
The canonical schema reference is the protocol spec;
the machine-readable version is openapi.json.
curl -X POST https://fregal-api.flamenco-dev.com/v1/agents -H 'content-type: application/json' -d '{"name":"my-agent"}'
# → { "api_key": "ufp_sk_..." } (shown once — store it)
# Send on every call: Authorization: Bearer ufp_sk_...
| Path | Auth | ||
|---|---|---|---|
| POST | /v1/agents | — | Self-serve API key issuance |
| GET | /v1/capabilities | — | Processes, materials, cuts, size/qty ranges — live JSON |
| POST | /v1/files | key | Upload artwork/STL/STEP/DXF (multipart file) or register a URL (JSON {url}) |
| POST | /v1/quotes | key | Fabrication request → ranked offers with prices, ETAs, previews. Streams: status "quoting" answers fast with the quickest vendors; the rest land on re-read |
| GET | /v1/quotes/:id | key | Re-read a quote — LIVE state: offers stream in while status is quoting; previews resolve asynchronously |
| POST | /v1/quotes/:id/refine | key | Narrow with added constraints (incl. a processes lane filter — null clears it), or change the spec ({spec:{material,color,quantities}} — lanes requote warm); empty body = re-read |
| POST | /v1/checkout | key | Create order + hosted payment link for the user |
| GET | /v1/orders | key | Your agent's recent orders (newest first) |
| GET | /v1/orders/:id | key | Status, ETA, tracking, event log |
| POST | /v1/orders/:id/review | key | Verified-purchase vendor review: {stars, comment?, facets?} — one per order, resubmit to update |
| GET | /v1/parts/:id | key | Inspect a part number; reorder via spec.part_number on /v1/quotes |
vendor_rating — {stars, review_count, facets}
built from verified-purchase reviews only. The count is always the honest number of reviews on file.POST /v1/attestations, vendor-key authed), and honest, cited
materials intelligence — specs, stronger/hotter/outdoor ladders, finishes,
caveats first (/docs/materials.md).POST /v1/quotes
{
"process": "decal", // or "fdm_print" — or omit: AFN detects
// the file kind and routes it (see below)
"spec": {
"file_id": "f_...", // from POST /v1/files
"size_in": { "width": 3, "height": 3 }, // decal, optional (default 3x3)
"cut": "triangle", // FREE-FORM — pass the user's words. Known cuts
// (contour|square|oval|kiss_cut) match exactly;
// anything else returns closest-match offers with
// a substitutions note, and the ask is logged
"material": "vinyl", // free-form too; omit = constraint-compliant options
"quantities": [1] // ask for what the user wants, even 1 — vendors
// with a higher minimum quote AT their minimum
// and set requested_quantity on the offer
},
"constraints": {
"ship_to_zip": "78701", // omit if unknown — prices stay all-in, shipped to
// the network's assumed US destination, declared
// on routing.ship_to { zip, assumed: true }
"deadline": "2026-07-13", // drop offers that can't arrive in time
"processes": ["sheetmetal"], // lane filter — quote ONLY these processes;
// on refine: inherited, and null clears it
// ("show everything again")
"outdoor_rated": true,
"submersible": true, // "must survive pool water"
"max_budget_cents": 3500 // all-in cap
}
}
For fdm_print: spec.file_id is an STL or STEP (converted
server-side); optional material (pla|petg|asa),
color, quantities.
For print: spec.file_id is print-ready artwork (PDF preferred,
PNG/JPEG accepted); optional product
(business_cards|flyers|posters — omit to quote every kind the artwork fits),
free-form size ("letter", "24x36"), free-form paper
("matte", "glossy", "recycled"), sides (single|double),
quantities. Deadline, budget, and durability constraints filter print
offers like any other process; product kinds, sizes, and stocks are discoverable
via GET /v1/capabilities.
For sheetmetal: spec.file_id is a DXF flat pattern or STEP model;
free-form material (canonical mild_steel|aluminum_5052|stainless_304 —
"steel", "5052", "stainless" resolve), optional thickness_in (quoted at the
nearest stocked gauge), optional units (mm|in, DXF only, default mm),
optional bend_count (> 0 prices CNC bending), free-form finish
(as_cut default, deburred, powder_coat),
quantities. Filterable criteria: outdoor_rated drops bare mild
steel (it rusts) while aluminum/stainless pass — and upgrades unpinned mild steel to a
protective powder_coat finish; submersible matches nothing (no
vendor claim); deadline and budget narrow as usual. Material attributes, per-material
thicknesses_in, and finishes are discoverable via
GET /v1/capabilities.
Omit process and AFN detects the file kind — artwork (png/jpg/svg/pdf),
stl, step, dxf — and quotes every process that can
make it, fanning out over a small default material set per process. The response adds a
routing object: {detected_file_kind, candidates, also_possible,
viewer_mesh_url}.
also_possible lists processes AFN can't quote yet (e.g. pcb)
with a reason; each is logged as a demand signal that drives vendor sourcing.
viewer_mesh_url (mesh/BREP uploads only) serves a face-classified binary
viewer mesh (AFVM v1) that derives lazily — poll it until 200.
Passing process explicitly behaves exactly as before.
price_basis: vendor_api (live vendor quote),
cached_matrix (AFN's continuously refreshed price table), or estimate
(geometry-based). Caveat estimates to your user. Every offer carries a preview —
the vendor's own product render of the artwork with those exact options; poll the quote until
preview.status is ready.POST /v1/checkout
{
"offer_id": "of_...",
"shipping_option_id": "express", // from the offer's shipping_options
"ship_to": { "name": "...", "street1": "...", "city": "...", "state": "TX", "zip": "78701" },
"contact": { "email": "buyer@example.com" }
}
# → { "order_id": "ord_...", "checkout_url": "https://checkout.stripe.com/...", "total_cents": 4230 }
Hand checkout_url to your user — never collect card details yourself. AFN is the
merchant of record and places the vendor order automatically after payment.
Lifecycle: pending_payment → paid → placing → placed → in_production → shipped → delivered.
AFN speaks standard MCP over Streamable HTTP — one endpoint serves ChatGPT, Claude, Gemini,
Cursor, Copilot, and every other MCP client. Tools:
get_fabrication_quote, refine_quote, create_checkout,
get_order_status, list_orders, leave_review.
ChatGPT additionally renders an embedded offer-comparison
widget; every other client gets the same results as complete text.
| Client | Setup |
|---|---|
| ChatGPT | Settings → Apps & Connectors → create a connector with the /mcp URL |
| Claude Code / Desktop / claude.ai | claude mcp add --transport http ufp <mcp-host>/mcp (or add a custom connector in claude.ai settings) |
| Gemini CLI | add {"ufp": {"httpUrl": "<mcp-host>/mcp"}} under mcpServers in ~/.gemini/settings.json |
| Cursor / VS Code Copilot / others | add a remote (Streamable HTTP) MCP server pointing at <mcp-host>/mcp |
| Custom agents (LangChain, Agents SDK, ADK…) | use plain REST with openapi.json — same funnel, same offers |
https URL or a base64 data: URL in the design_file
tool parameter — AFN fetches and stores it either way.