---
title: "Get ranked fabrication offers"
method: POST
path: "/v1/quotes"
---

# Get ranked fabrication offers

`POST /v1/quotes`

## Request

- Base URL: `https://agenticfabricationnetwork.ai`
- URL: `https://agenticfabricationnetwork.ai/v1/quotes`
- Auth: HTTP bearer

## Request body

- object
  - `process` 'fdm_print' | 'resin_print' | 'powder_print' | 'metal_print' | 'elastomer_print' | 'cnc' | 'sheetmetal' | 'laser_cut' | 'decal' | 'print' | 'apparel': Optional. Omit it and AFN detects the file kind (artwork/stl/step/dxf) and quotes every process that can make it — the response's routing object lists candidates and also_possible
  - `spec` object, required: Provide file_id (new design) OR part_number (reorder of a previous paid order)
    - `file_id` string: Required unless part_number is set
    - `part_number` string: AFN part number (UFP-… / part_…) from a previous paid order — reorders reuse the stored design and spec; other fields here override the stored ones
    - `share_key` string: Required when reordering a LOCKED part: the k=… value from the owner's share link
    - `attachments` object[]: Additional files for the SAME part — the PDF drawing next to the STEP, spec sheets, BOMs (each from POST /v1/files). role is optional: omitted, AFN infers it from the file kind (artwork alongside geometry = drawing; a second geometry file = aux). Attachments never route or price a quote; they persist with the part, so reorders replay the full document set
      - `file_id` string, required
      - `role` 'drawing' | 'spec_sheet' | 'bom' | 'centroid' | 'aux'
    - `size_in` object: decal only; default 3x3
      - `width` number
      - `height` number
    - `cut` string: decal only, FREE-FORM — pass the user's words ("triangle", "die cut"). Known cuts (contour|square|oval|kiss_cut) match exactly; unknown asks return closest-match offers with substitutions. Omit to compare the main options
    - `material` string: decal: vinyl|holographic|transparent|glitter (free-form, closest-match like cut); fdm_print: pla|petg|asa
    - `color` string: fdm_print filament color
    - `quantities` integer[]: One offer per quantity. Ask for what the user wants (even 1) — vendors with a higher minimum quote at their minimum and set requested_quantity on the offer. A part_number reorder with no quantities defaults to the quantity last purchased
  - `constraints` Constraints, required
    - `ship_to_zip` string: Destination US ZIP code. Omit if the buyer never stated one — the network stamps its assumed central-US destination (prices stay all-in with shipping) and declares it (routing.ship_to_zip + routing.ship_to { zip, assumed: true })
    - `ship_to_country` string
    - `deadline` string, date, nullable: Latest acceptable delivery date; offers that miss it (and nothing else) move to near_misses with their real ETA — never into offers[]. On refine, explicit null CLEARS an inherited deadline (near-misses convert back); omission inherits it
    - `processes` string[], nullable: Process-lane filter: quote ONLY these processes (intersected with routing candidates). Absent = no filter; on refine the child inherits the parent's filter, and an explicit null CLEARS an inherited filter ("show everything again"). Narrowing by process is a constraint, never a fresh quote — warm vendor sessions survive it.
    - `outdoor_rated` boolean, nullable
    - `submersible` boolean, nullable
    - `high_temp` boolean, nullable: Must tolerate sustained heat (vendor heat-resistance claim)
    - `food_safe` boolean, nullable: Must be vendor-certified food safe
    - `max_budget_cents` integer, nullable: All-in budget cap, USD cents. On refine, explicit null CLEARS an inherited cap
    - `requirements` string[], nullable: Catch-all requirements channel: verbatim terms from the buyer's conversation ("UV resistant", "anodized: red", "iso 13485", "tolerance: 0.1mm"). Each term resolves deterministically — applied terms affect offers, unresolved terms come back in the quote's notes and are logged as demand for vendor sourcing. Terms never override explicit spec/constraint fields and are never an error; there is no wrong place to put a term.
    - `callouts` object[]: Drawing callouts the buyer's agent extracted — dimensions that matter. The tightest tolerance_mm prunes vendors whose published tolerance claim cannot hold it; vendors with no tolerance claim on record are excluded when a tolerance is demanded.
      - `name` string, required: What the dimension is, in the agent's words ("hole_diameter")
      - `value` number, required
      - `unit` 'mm' | 'in', required
      - `tolerance_mm` number: Required tolerance for this dimension, in mm (±)
      - `critical` boolean: Marked critical on the drawing (inspection-worthy)
      - `note` string
    - `reliability` object, nullable: Vendor-reliability floor — narrows to vendors with a demonstrated track record. Like every constraint it narrows, never widens — except explicit null on a refine, which CLEARS the whole inherited floor.
      - `min_stars` number: Only vendors whose displayed rating is at least this — vendors with no reviews yet are excluded
      - `min_reviews` integer: Only vendors with at least this many verified-purchase reviews
      - `proven_spec_orders` integer: Only vendors with at least N completed orders for this exact process+material — "a vendor that prints PC reliably"

## Response `201`

Ranked offers

- QuoteResponse
  - `quote_id` string
  - `parent_quote_id` string, nullable
  - `scenario` string: Comparison-scenario label this quote was refined under (see the refine body's `scenario`). Echoed on every read of a labeled child so pollers can bucket side-by-side offers by scenario. Absent on unlabeled quotes.
  - `process` 'fdm_print' | 'resin_print' | 'powder_print' | 'metal_print' | 'elastomer_print' | 'cnc' | 'sheetmetal' | 'laser_cut' | 'decal' | 'print' | 'apparel'
  - `status` 'open' | 'quoting' | 'complete' | 'partial' | 'expired' | 'ordered': Quotes STREAM: "quoting" = the fastest vendors' offers are in this response and more are landing — re-read GET /v1/quotes/{id} (routing.expected_vendors says who is still pricing). "complete" = every vendor answered; "partial" = some lanes failed or were superseded but every offer shown is real and orderable.
  - `offers` object[]: Ranked best-first (price + speed) — unless an `order_by` applies, in which case they arrive already sorted by it (the echoed order_by says which).
    - `id` string
    - `quote_id` string
    - `vendor` string
    - `vendor_display_name` string
    - `vendor_logo_url` string: The vendor's brand mark (small square image URL) for offer cards. Absent when AFN has no curated mark for the vendor — render the name alone, never a placeholder image
    - `process` 'fdm_print' | 'resin_print' | 'powder_print' | 'metal_print' | 'elastomer_print' | 'cnc' | 'sheetmetal' | 'laser_cut' | 'decal' | 'print' | 'apparel'
    - `spec_resolved` object: Exact size/cut/material/color the vendor will make
    - `quantity` integer
    - `requested_quantity` integer: Present when the vendor's minimum order forced quantity above what was requested — e.g. asked for 1, minimum is 25
    - `substitutions` object[]: Present when this offer is a closest-match stand-in for an unsupported ask, e.g. {field:"cut", requested:"triangle", provided:"contour"}. The ask is logged for future vendor sourcing.
      - `field` string
      - `requested` string
      - `provided` string
    - `min_order` object: Present when a vendor per-order charge shaped this price — an order-value floor (the subtotal is the vendor's per-order minimum, not a scaled per-part price; floors billed as a cart top-up on the shipping line count too) or a flat per-order startup fee (see kind). threshold_cents is the vendor-side amount in USD cents; detail is the vendor's published wording. Absent = the price scaled from the part.
      - `threshold_cents` integer
      - `detail` string
      - `kind` 'min_purchase' | 'startup_fee': Which per-order charge shaped the price: "min_purchase" (default when absent) = the vendor floors the order value; "startup_fee" = a flat per-order fee added to every order and already inside the subtotal (threshold_cents is that fee).
    - `unit_price_cents` integer: All-in unit price (payment-processing gross-up baked in — no separate fee line ever)
    - `subtotal_cents` integer
    - `shipping_options` object[]
      - `id` string
      - `label` string
      - `cost_cents` integer: All-in shipping cost, USD cents (payment-processing gross-up baked in; free shipping stays 0)
      - `eta_date` string, date
      - `eta_business_days` integer
    - `total_from_cents` integer: Subtotal + cheapest shipping
    - `preview` object: Product preview for these exact options. Resolves asynchronously — poll GET /v1/quotes/{id}.
      - `status` 'pending' | 'ready' | 'failed' | 'unavailable'
      - `url` string, uri
      - `source` 'vendor_render' | 'ufp_render'
    - `attributes` object: Only vendor-advertised claims appear; an absent flag means "no claim", never "no". OPEN vocabulary: beyond the named keys, any additional boolean key is a vendor-advertised claim (e.g. iso_13485: true) — same honest-claims rule
      - `outdoor_rated` boolean
      - `submersible` boolean
      - `high_temp` boolean
      - `food_safe` boolean
    - `vendor_rating` object: Vendor star rating built from verified-purchase reviews only (see POST /v1/orders/{id}/review)
      - `stars` number
      - `review_count` integer: Honest count of reviews on file — 0 means no one has reviewed yet
      - `facets` object: Facet averages over reviews that scored them
        - `quality` number
        - `timeliness` number
        - `accuracy` number
      - `reliability` object[]: Per-process/material track record ("prints PC reliably") — filterable via constraints.reliability
        - `process` 'fdm_print' | 'resin_print' | 'powder_print' | 'metal_print' | 'elastomer_print' | 'cnc' | 'sheetmetal' | 'laser_cut' | 'decal' | 'print' | 'apparel'
        - `material` string
        - `orders` integer: Honest sample size — completed orders for this process+material
        - `stars` number
        - `on_time_rate` number
    - `price_basis` 'vendor_api' | 'cached_matrix' | 'estimate'
    - `fulfillment` 'connected' | 'not_connected': Whether UFP's checkout reaches this vendor. "connected" = a real paid order has gone end to end down this vendor+process lane. "not_connected" = the price is real but ordering isn't wired through yet; POST /v1/checkouts refuses it before any money moves.
    - `expires_at` string, date-time
  - `order_by` object: The presentation ordering offers[] is currently sorted by (see the refine body's `order_by`), echoed on every read while one applies. Absent = the default recommended (price + speed) rank.
  - `steps` object[]: The last few refine steps behind this quote, NEWEST FIRST (ALC-127) — steps[0] is the step that produced this quote (what undo_last_step takes back). Computed from stored quote states on every read, capped at 3. Absent on original quotes.
    - `quote_id` string, required: The child quote this step produced
    - `parent_quote_id` string, required: The quote the step refined — its stored state is what undo restores
    - `scenario` string: Present when this step opened a comparison-scenario lane
    - `summary` string, required: Human one-liner for the step ("added deadline 2026-09-05; material pla → asa")
    - `changes` object[], required: Field-level machine diff: constraint keys plus "spec.<key>" for spec deltas. `from` absent = the field wasn't set before; `to` absent = the step cleared it.
      - `field` string, required
      - `from` unknown
      - `to` unknown
  - `near_misses` object[]: Real bids that missed exactly one hard constraint, closest-first (capped at 6 per response) — shown for context, never mixed into offers[], never orderable as-is
    - `id` string
    - `quote_id` string
    - `vendor` string
    - `vendor_display_name` string
    - `vendor_logo_url` string: The vendor's brand mark (small square image URL) for offer cards. Absent when AFN has no curated mark for the vendor — render the name alone, never a placeholder image
    - `process` 'fdm_print' | 'resin_print' | 'powder_print' | 'metal_print' | 'elastomer_print' | 'cnc' | 'sheetmetal' | 'laser_cut' | 'decal' | 'print' | 'apparel'
    - `spec_resolved` object: Exact size/cut/material/color the vendor will make
    - `quantity` integer
    - `requested_quantity` integer: Present when the vendor's minimum order forced quantity above what was requested — e.g. asked for 1, minimum is 25
    - `substitutions` object[]: Present when this offer is a closest-match stand-in for an unsupported ask, e.g. {field:"cut", requested:"triangle", provided:"contour"}. The ask is logged for future vendor sourcing.
      - `field` string
      - `requested` string
      - `provided` string
    - `min_order` object: Present when a vendor per-order charge shaped this price — an order-value floor (the subtotal is the vendor's per-order minimum, not a scaled per-part price; floors billed as a cart top-up on the shipping line count too) or a flat per-order startup fee (see kind). threshold_cents is the vendor-side amount in USD cents; detail is the vendor's published wording. Absent = the price scaled from the part.
      - `threshold_cents` integer
      - `detail` string
      - `kind` 'min_purchase' | 'startup_fee': Which per-order charge shaped the price: "min_purchase" (default when absent) = the vendor floors the order value; "startup_fee" = a flat per-order fee added to every order and already inside the subtotal (threshold_cents is that fee).
    - `unit_price_cents` integer: All-in unit price (payment-processing gross-up baked in — no separate fee line ever)
    - `subtotal_cents` integer
    - `shipping_options` object[]
      - `id` string
      - `label` string
      - `cost_cents` integer: All-in shipping cost, USD cents (payment-processing gross-up baked in; free shipping stays 0)
      - `eta_date` string, date
      - `eta_business_days` integer
    - `total_from_cents` integer: Subtotal + cheapest shipping
    - `preview` object: Product preview for these exact options. Resolves asynchronously — poll GET /v1/quotes/{id}.
      - `status` 'pending' | 'ready' | 'failed' | 'unavailable'
      - `url` string, uri
      - `source` 'vendor_render' | 'ufp_render'
    - `attributes` object: Only vendor-advertised claims appear; an absent flag means "no claim", never "no". OPEN vocabulary: beyond the named keys, any additional boolean key is a vendor-advertised claim (e.g. iso_13485: true) — same honest-claims rule
      - `outdoor_rated` boolean
      - `submersible` boolean
      - `high_temp` boolean
      - `food_safe` boolean
    - `vendor_rating` object: Vendor star rating built from verified-purchase reviews only (see POST /v1/orders/{id}/review)
      - `stars` number
      - `review_count` integer: Honest count of reviews on file — 0 means no one has reviewed yet
      - `facets` object: Facet averages over reviews that scored them
        - `quality` number
        - `timeliness` number
        - `accuracy` number
      - `reliability` object[]: Per-process/material track record ("prints PC reliably") — filterable via constraints.reliability
        - `process` 'fdm_print' | 'resin_print' | 'powder_print' | 'metal_print' | 'elastomer_print' | 'cnc' | 'sheetmetal' | 'laser_cut' | 'decal' | 'print' | 'apparel'
        - `material` string
        - `orders` integer: Honest sample size — completed orders for this process+material
        - `stars` number
        - `on_time_rate` number
    - `price_basis` 'vendor_api' | 'cached_matrix' | 'estimate'
    - `fulfillment` 'connected' | 'not_connected': Whether UFP's checkout reaches this vendor. "connected" = a real paid order has gone end to end down this vendor+process lane. "not_connected" = the price is real but ordering isn't wired through yet; POST /v1/checkouts refuses it before any money moves.
    - `expires_at` string, date-time
    - `excluded_by` object: Why this bid sits outside the constraints, with machine fields
      - `reason` 'attribute' | 'deadline' | 'budget', required
      - `field` string: The constraint field/demand key that excluded it
      - `requested` string: What the buyer asked for (stringified)
      - `detail` string, required: Human wording — identical to the quote note
      - `earliest_eta` string, date: Deadline misses: soonest the part could actually arrive
      - `days_late` integer: Deadline misses: calendar days earliest_eta lands past the deadline
      - `cents_over` integer: Budget misses: cheapest all-in total minus max_budget_cents
  - `routing` object: How the quote was dispatched (always includes expected_vendors; candidates/also_possible reflect auto-routing when `process` was omitted)
    - `detected_file_kind` string: What AFN detected the uploaded file to be (artwork, stl, step, dxf)
    - `candidates` string[]: Processes quoted live in this response
    - `also_possible` object[]: Processes that could make this part but have no vendor on AFN yet — each is logged as a demand signal for vendor sourcing
      - `process` string
      - `reason` string
    - `expected_vendors` object[]: Who is STILL PRICING on this quote — live per read: lanes that settled (offers landed, no bid, failed, or superseded) drop out on the next read. Render placeholder tiles from it while status is "quoting". Health-down vendors are never dispatched, so never listed.
      - `vendor` string
      - `process` 'fdm_print' | 'resin_print' | 'powder_print' | 'metal_print' | 'elastomer_print' | 'cnc' | 'sheetmetal' | 'laser_cut' | 'decal' | 'print' | 'apparel'
      - `logo_url` string: The vendor's brand mark for the placeholder tile (same sourcing as offer.vendor_logo_url); absent when uncurated
      - `expected_wait_s` number: Expected answer time in seconds: the lane's p90 real answer latency over the last 14 days (plus queue delay behind the dispatch pool), clamped 3-75s — sized so the answer lands before a bar paced with it ends, nearly always; falls back to the process-pooled p90 (all vendors on the process), then coarse 2/8/20s health-probe buckets. Pace loading animations with it — never print it as a promise or complete a bar before the offer lands
    - `viewer_mesh_url` string: Face-classified viewer mesh for mesh/BREP uploads (stl/step/iges): a binary AFVM v1 blob — magic "AFVM", version, JSON header (bbox, classified faces: planes/cylinders with diameter+axis, holes), then little-endian Float32 positions, Uint32 indices, and Uint32 face-id-per-triangle. Derives lazily, so poll until 200 (the URL serves CORS on both 200 and 404). Absent for kinds with no mesh representation.
  - `ufp_url` string: Public download of this quote's .ufp part container: a plain STORE-mode ZIP holding ufp.json (the quote's CURRENT spec + constraints, pretty-printed) and design/<filename> (the design bytes verbatim) — the all-in-one part file. Re-uploadable anywhere a design file is accepted: its saved intent applies automatically as fill-if-absent defaults. Stable per quote id (refines mint child ids). Present whenever the quote has a design file.
  - `design_url` string: Public download of the bare design file inside that container (the .step/.stl/artwork the network actually quotes) — 'save just the .step' without unzipping. Present alongside ufp_url.
  - `notes` string[]: Why vendors/offers were excluded, vendor outages, etc.
  - `created_at` string, date-time

---

[API](https://skmtc.dev/agenticfabricationnetwork/apis/afn-agentic-fabrication-network.md) · [All operations](https://skmtc.dev/agenticfabricationnetwork/apis/afn-agentic-fabrication-network/llms.txt) · [OpenAPI document](https://skmtc.dev/agenticfabricationnetwork/apis/afn-agentic-fabrication-network/revisions/42c16babcd2b?raw)
