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://agenticfabricationnetwork.ai/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,finish,quantities}} — lanes requote warm; a finish rides finish-capable lanes, printer lanes keep the color); empty body = re-read |
| POST | /v1/quotes/:id/preview | key | The SOFT quote: refine's exact body, planned but never launched — how many distinct fabricators (and which lanes) the network would ask for the refined part, plus the exclusion notes. Creates nothing (no child quote, no vendor call, no supersede, no demand row): fire one per edit. 200, never 201 |
| 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 |
| GET | /quotes/:id/design | — | Download just the bare design file |
| GET | /v1/ufp/spec.schema.json | — | JSON Schema of the .ufp part spec (ufp.json spec: material, color, finish, quantities, thickness_in, tapping, inserts, bends) — the exact record shapes refine accepts. Manifest schema: /v1/ufp/ufp.schema.json |
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" },
"coupon_code": "AFN-XXXXXX" // optional — only if the user gave you one
}
# → { "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.