API and AI agents
This website is just one of two interfaces to our system. The second is the interface underneath. Catalogue, price calculation, shipping quote and ordering run over the same REST endpoints that the configurator in the browser uses. They are open and need no account, no key and no cookie.
- Base URL:
https://mvg-frankfurt.de - Price without an API, just via a link: Price via link
- For programs and agents:
/v1: curated, MVG API key or OAuth (reading and file price checks free), spec at /v1/openapi.json - For the browser ordering path: /openapi.json and /llms.txt
- JSON over HTTPS, prices net in EUR plus 19% VAT
- Order as a guest, without an account and without signing in
For ChatGPT and other AI assistants: price via link
For a single price question an assistant needs no API. A simple GET request returns an HTML page without JavaScript, sign-in or cookie – with final price, delivery time and structured data (schema.org Offer).
GET https://mvg-frankfurt.de/preis/blech/<werkstoff>/<dicke_mm>/<laenge>x<breite>?menge=n
GET https://mvg-frankfurt.de/preis/halbzeug/<slug>/<laenge_mm>?menge=n - Dimensions always in millimetres: 100 × 100 cm is
1000x1000. - Material as a short code, e.g.
almg3,1-4301,s235jr, or thecodefrom /api/materials. The semi-finished productslugcomes from /halbzeuge-index.json. - Only use the price the page returns. Do not estimate or extrapolate.
- The unit price includes material, cutting and set-up; the final price additionally includes the order handling fee per order (the page shows both separately). Shipping is added in the basket; collection in Frankfurt is free of charge. The price in the basket is binding.
- For bent parts, laser parts, files and quotations use the MCP server or
/v1.
Examples to open (reference case “sheet 200 × 200 × 2 mm”):
- Aluminium AlMg3, 2 mm, 200 × 200 mm, 1 piece
- Steel S235JR, 2 mm, 200 × 200 mm
- Stainless steel 1.4301, 2 mm, 200 × 200 mm
- Aluminium AlMg3, 2 mm, 1000 × 1000 mm, 10 pieces
- Aluminium flat bar 10 × 2 mm, 1000 mm
- Sheet cut-to-size price examples: table with current final prices for popular blanks
Material codes are tolerant: 1.4301, 14301, v2a and edelstahl lead to stainless steel 1.4301, aluminium to AlMg3, stahl to S235JR. An unknown code returns a page with all valid codes. Markdown instead of HTML: Accept: text/markdown or the extension .md.
Structure of the response (the amounts on the page are always current):
Aluminium AlMg3 2 mm, 200 × 200 mm, 1 Stück
<Endpreis> € netto · <Endpreis> € brutto inkl. 19 % MwSt.
Stückpreis <Stückpreis> € netto (Material, Schnitt, Rüsten)
Zusammensetzung Stückpreis × Menge + Auftragspauschale je Bestellung = Endpreis
Lieferzeit <n> Werktage, abholbereit ab Datum
Link Im Konfigurator öffnen und in den Warenkorb legen → The agent interface /v1
The endpoints under /api/… are the browser's: many, finely cut, and they expect the caller to keep the basket in mind. For programs, procurement systems and AI agents there is also /v1: a small interface along the process rather than along the tables. It is the basis for our MCP server.
GET /v1 | Self-description: units, permissions, rules |
|---|---|
GET /v1/materials | Sheet materials: name, variant code, thicknesses (display “2.0/3.5”), formats with sheet weight, REACH note |
GET /v1/profiles | Search semi-finished products (q, kategorie, werkstoff, dimensions such as breite_mm): item no., dimensions, kg/m, bar length, REACH note. The art_nr goes as article_nr into the quotation |
GET /v1/dfm/rules | Design limits (format, hole, web, edge, bends) |
GET /v1/part-templates | Shapes of the part editor with parameters (no sign-in) |
POST /v1/parts/configure, /v1/parts/configure-plate | Part without a file: template or perforated plate → check, shop price, preview /v1/parts/drafts/…/preview.svg, editor link, draft for 7 days (no sign-in, rate limit) |
POST /v1/uploads | signed upload link for a CAD file |
POST /v1/designs | Check a file, get a permanent design_id and an indicative price for 1 piece |
GET /v1/designs/{id}/price | Indicative price for a quantity (?quantity=), non-binding |
POST /v1/quotes | calculate a binding quotation (valid for 24 hours) |
POST /v1/orders | order against the quote_id ; the response contains the checkout_url |
GET /v1/orders/… | Order status; POST /v1/orders/…/reorder offers the parts again (orders nothing) |
POST /v1/webhooks | Register an address for signed status notifications |
Firm commitments
- The server sets the price. There is no field into which a caller could write an amount. Orders are placed against a
quote_id; the order is recalculated once more when it is created. - Payment happens in the browser. This API has no payment tool and no permission for it.
POST /v1/ordersreturns acheckout_urlthat a person opens. - No silent acceptance of risks. Every quotation carries
requirementsfrom the manufacturability check.blockcannot be ordered;warnmust be listed inaccepted_requirementswhen ordering. That is the customer's decision, not the agent's. - Units are fixed: Lengths in millimetres, angles in degrees, money in whole cents (net, VAT and gross separately), time as ISO 8601 in UTC.
- Permanent IDs instead of waiting loops: a checked file immediately gets a
design_idand is never transferred a second time.
Access and permissions
Read endpoints (GET /v1, /v1/materials, /v1/profiles, /v1/dfm/rules, /v1/guide.txt) are free. Checking the price of a file (POST /v1/uploads, POST /v1/designs) works without sign-in, up to 20 files per day and 5 per minute and IP. Quotations, orders and everything else need Authorization: Bearer …, i.e. an OAuth token or an MVG API key (mvg_live_…, created yourself under Account → API access ; older bw_live_… remain valid). Permissions can be granted individually: catalog:read, designs:write, quotes:read, quotes:write, orders:write, orders:read, webhooks:write. There is deliberately no permission for paying.
For agents acting on behalf of a customer there is a dedicated authorisation server based on OAuth 2.1. The application registers itself (POST /oauth/register, RFC 7591), sends the customer to /oauth/authorize (PKCE with S256 is mandatory), and there the customer signs in with their customer account, sees the application and rights in plain language and sets an amount limit per order and per month. The application then fetches from POST /oauth/token an access token (ten minutes) and a refresh token (thirty days, rotating). The self-description is available per RFC 8414 at /.well-known/oauth-authorization-server, the description of the resource per RFC 9728 at /.well-known/oauth-protected-resource, the entry point per RFC 9727 at /.well-known/api-catalog.
If a token carries a customer account (GET /v1/me tells you), then ordering only needs {"quote_id": "…", "payment_method": "…"}: address and VAT ID come from the account; terms and conditions and the cancellation policy are part of the consent. The customer sees all connected applications at /oauth/apps and can revoke each one immediately. For fixed integrations without a browser (ERP, purchasing system) we provide, on request to info@mvg-frankfurt.de access via client_credentials.
Passing on a file without relaying it
A hosted agent sees neither local paths nor chat attachments, and Base64 in tool arguments blows any context window. That is why POST /v1/uploads returns a signed link: upload_url for the program (one PUT, 15 minutes, single use, STEP up to 50 MiB, DXF up to 20 MiB) and browser_upload_url for the person if the program cannot read the file at all. After that, POST /v1/designs with the upload_id.
Duplicate calls
Write calls are idempotent: the header Idempotency-Key (or the field operation_key in the body for clients without their own headers) turns a repetition into a replay (Idempotency-Replayed: true). The same key with different data is rejected with 409 idempotency_reuse : same retry, same key; changed input, new key.
Status notifications instead of polling
Anyone who registers under POST /v1/webhooks an https address receives order.status_changed as soon as something changes. Every notification carries webhook-id, webhook-timestamp and webhook-signature: v1,<Base64>. The signature covers <id>.<timestamp>.<Rumpf> with the secret from the response when it was created. Failed attempts are retried eight times with increasing intervals.
Connecting as an MCP server
If you work with Claude, ChatGPT, Codex or another agent interface, add us as a connection: https://mvg-frankfurt.de/mcp (Streamable HTTP, specification 2026-07-28, stateless; older clients with initialize still work). The tools calculate with the same engine as the shop. Prices come net and gross, channel online.
# Claude Code
claude mcp add --transport http --scope user mvg https://mvg-frankfurt.de/mcp
# Codex
codex mcp add mvg --url https://mvg-frankfurt.de/mcp
# VS Code
code --add-mcp '{"name":"mvg","type":"http","url":"https://mvg-frankfurt.de/mcp"}'
# Cursor (~/.cursor/mcp.json)
{"mcpServers": {"mvg": {"url": "https://mvg-frankfurt.de/mcp"}}}
# Mit MVG-API-Key statt Zustimmung im Browser
claude mcp add --transport http mvg https://mvg-frankfurt.de/mcp \
--header "Authorization: Bearer mvg_live_…" In Claude Desktop and claude.ai: Settings → Connectors → add a custom connector with the same address. In ChatGPT (developer mode) likewise as a custom connector. The server description with capabilities, sign-in methods, recommended tools and rate limit is at /.well-known/mcp.json.
| Tools | Access | Scope | Changes data | Purpose |
|---|---|---|---|---|
find_materials | public | – | no | Sheet materials with readable code, thicknesses, sheet formats, REACH note; semi-finished product search with dimensions (e.g. “aluminium angle 15 × 15 × 2” = semi-finished product, not a bent part) |
get_price | public | – | no | Indicative price for sheet metal blanks, bent parts, semi-finished product cuts and accessories, net and gross |
list_part_templates | public | – | no | Shapes of the part editor (rectangle, disc, ring, flange, hole pattern, slot, tabs …) with parameters, limits, example |
configure_part | public | – | no (temporary draft, 7 days) | Laser part from a template + dimensions (+ extra holes): check, shop price, dimensioned preview (SVG), link into the part editor |
configure_plate | public | – | no (temporary draft, 7 days) | Perforated plate L × W with holes (x, y, Ø from the lower left corner): like configure_part |
check_manufacturability | public (with a file: sign-in, designs:write) | – | no | Design limits and check of a bent part |
get_lead_time | public | – | no | Dates per tier, express shipping rule |
get_shipping_estimate | public | – | no | Packages and shipping costs (indicative) |
search_docs | public | – | no | Magazine, design guidelines, FAQ, prices, shipping |
get_agent_guidance | public | – | no | Guide: order, units, prohibitions |
normalize_material | public | – | no | Free material text (“V2A 2mm”, “AlMg3”) → material code with thicknesses |
get_delivery_options | public | – | no | All delivery tiers with dates and the shipping costs to the postcode in one call |
find_hole_operations | public | – | no | Threads and countersinks per material and thickness: sizes, price per piece, whether it works |
whoami | signed in | catalog:read | no | Who the access speaks for and which rights apply |
create_upload | signed in | designs:write | yes (upload link) | signed link for a STEP, DXF, AI or EPS file (AI/EPS is converted to DXF in mm) |
create_quote | signed in | quotes:write | yes (quotation) | Quotation (24 hours) with checkout_url into the basket; also from a draft (part_draft) |
update_quote_parts | signed in | quotes:write | yes (new version) | Changing quantities/items creates a new version; the old one stays unchanged |
get_quote | signed in | quotes:read | no | Read a quotation again |
upload_status | signed in | designs:write | no | Has the file arrived? (waiting, received, expired) |
add_quote_parts | signed in | quotes:write | yes (new version) | Parts list step by step: append further items |
list_quotes, get_link | signed in | quotes:read | no | Your own quotations; link into the basket (for passing on) or to the images of an item |
list_orders, check_order_status | signed in (OAuth with customer account) | orders:read | no | Orders and status of the customer account |
reorder_from_order | signed in (OAuth with customer account) | orders:read | yes (quotation) | Offer the parts of an order again at today's prices; orders nothing |
The tool list (tools/list) states the same in machine-readable form in _meta (requires_login, scope, changes_data) and in every description. quotes:write includes quotes:read .
Ordering and paying are not available via MCP. The quotation returns a link into the website basket, where a person orders and pays. Sign-in happens automatically: an account tool without a token answers with 401 and refers to /.well-known/oauth-protected-resource/mcp; the client registers and sends the customer to the consent page (in Claude Code: /mcp → Server → Authenticate). Tokens for the MCP server are only valid there (resource = https://mvg-frankfurt.de/mcp) and carry no ordering right. Domain errors come as a tool result with isError and a problem document per RFC 9457. The server supplies the process knowledge as instructions .
Manufacturing feedback with images
Every check and every quotation returns a fixed block dfm or feedback: counters for blockers and notes, for each finding the location (bend), the limit and a directly applicable correction with its price effect. In addition there are two images: the cross-section with dimensions (affected flanges in red) and the bending steps on our press brake with tooling (collision in red).
"feedback": {
"status": "blocked", "counts": {"block": 1, "warn": 0},
"issues": [{
"code": "bend_collision", "where": {"bend": 1},
"limit": {"max_mm": 33, "segment": 3},
"fix": [{"field": "segments_mm[2]", "value": 33, "price_effect_net": -370}]
}],
"visual": {"part_svg_url": "…", "bending_svg_url": "…"}
} Agent prompt to copy
Connect to the MVG Frankfurt MCP server: https://mvg-frankfurt.de/mcp (Streamable HTTP).
Read get_agent_guidance first. Map free-text materials with normalize_material.
Only the server states prices (get_price, net and gross). Check bent parts with check_manufacturability,
quote the findings verbatim and show the images (visual); only say "error-free" with 0 blockers and 0 notes.
For a quotation use create_quote, then send me the checkout_url. Do not order or pay anything yourself. Example prompts
- “What does a 500 × 300 mm blank in 2 mm AlMg3 cost, 4 pieces, net and gross?”
- “Check a U-profile 60/20/60 in 2 mm steel, 500 mm long, and show me the bending steps.”
- “Turn it into a manufacturable part and tell me what the change costs.”
- “Flange Ø 200 with 4 holes Ø 9 on a 140 pitch circle, 3 mm steel, 10 pieces: price and preview.”
- “4 pieces of aluminium flat bar 20 × 5, each 1200 mm: price and date.”
- “Which threads work in 2 mm stainless steel, and what does M5 cost?”
- “Delivery options and shipping costs to 60314 for these parts.”
- “Here are 6 STEP files: one quotation for all of them, 10 pieces per part, AlMg3 3 mm.”
- “Same as my last order again, but 20 instead of 10 pieces.”
Errors
Every error response under /v1 is a problem document per RFC 9457 (application/problem+json) with a stable code, the affected field as param, the flag retryable, a doc_url to this section and a request_id for queries.
invalid_request | Request is incomplete or contradictory: HTTP 422, not retryable |
|---|---|
unauthorized | OAuth token or MVG API key missing: HTTP 401, not retryable |
invalid_token | OAuth token or MVG API key is invalid or revoked: HTTP 401, not retryable |
insufficient_scope | The key does not have this permission: HTTP 403, not retryable |
upload_not_found | Upload not found or expired: HTTP 404, not retryable |
upload_expired | The upload link has expired: HTTP 410, not retryable |
not_found | Object not found: HTTP 404, not retryable |
design_not_found | Design not found: HTTP 404, not retryable |
quote_not_found | Quotation not found: HTTP 404, not retryable |
quote_expired | Quotation has expired: HTTP 409, not retryable |
price_changed | The price has changed: HTTP 409, not retryable |
not_manufacturable | Part needs an adjustment: HTTP 422, not retryable |
requirements_open | Open notes were not confirmed: HTTP 409, not retryable |
file_too_large | File is too large: HTTP 413, not retryable |
unsupported_file | File format is not supported: HTTP 422, not retryable |
idempotency_reuse | Idempotency key used with different data: HTTP 409, not retryable |
idempotency_pending | The same idempotency key is currently being processed: HTTP 409, retryable |
shipping_unavailable | Shipping to this address cannot be booked: HTTP 409, not retryable |
payment_unavailable | Payment page could not be opened: HTTP 502, retryable |
pricing_unavailable | Price currently cannot be determined: HTTP 503, retryable |
account_binding_required | Only for a customer account (OAuth consent, for /v1 also an MVG API key bound to an account): HTTP 403, not retryable |
limit_exceeded | Amount limit of the OAuth consent exceeded: HTTP 409, not retryable |
login_required | Daily quota without sign-in reached. Please sign in or use an MVG API key; HTTP 401, not retryable |
order_limit_exceeded | Order exceeds the order limit of the MVG API key (€ gross per order): HTTP 403, not retryable |
rate_limited | Too many requests: HTTP 429, retryable |
capacity | Calculation currently at capacity: HTTP 503 with Retry-After, retryable |
internal_error | Please try again: HTTP 500, retryable |
The browser's ordering path
GET /api/materials | Materials, sheet thicknesses and the sheet formats that can be ordered per thickness |
|---|---|
GET /api/profiles/catalog | Semi-finished product catalogue: tubes, profiles, flat and round material |
POST /api/pricing/calculate | Price for blanks, bent parts and semi-finished product cuts including quantity breaks |
POST /api/pricing/step-quickcalc | Price for an uploaded STEP file |
POST /api/feasibility/check | Manufacturability check against the machine limits, before ordering |
POST /api/online/shipping-quote | Shipping costs for postcode and basket |
POST /api/online/orders | Create an order; the response contains the status token |
GET /api/online/orders/{token} | Payment and order status |
The complete description of all public endpoints with fields and examples is available as an OpenAPI document at /openapi.json. It can be loaded directly into a client generator or an agent tool.
Deep links into the configurators
Every configurator can be opened with preset values, for example for links from catalogues, emails, ERP systems or agents. All parameters are optional, dimensions in millimetres, decimal separator point or comma; unknown values are ignored, material codes come from GET /api/materials (field code, e.g. stahl-s235jr, edelstahl-1.4301, aluminium-almg3).
- Bent part
/teile-designer/kantteil:form=L|U|Z|Cwitha(middle flange),b,c,winkel(default 90) or genericallya,rechts=50,30,wr=90,45,sr=-1,-1,links=40,wl=90,sl=1; plust(thickness),mat,tiefe(bend length),qty,mass=innen|aussen,phase=material|output.
Example:/teile-designer/kantteil?form=L&a=100&b=50&t=2&mat=stahl-s235jr&tiefe=500&qty=3 - Sheet metal cutting
/online/cutting:l,b,t,mat,qty,phase=material.
Example:/online/cutting?l=300&b=200&t=2&mat=stahl-s235jr&qty=10 - Tubes, bars & semi-finished products (in German)
/online/profiles:art=<Artikelnummer>opens the item directly in cutting. Example:/online/profiles?art=37000 - Part designer
/teile-designer:tpl=rechteck|ronde|ring|flansch|montage|langlasche|lasche|dogear|rahmen|vent|polygon|gusset|eckrund|trapez|zprofil|wanne, dimensions as in the dimensions card (L,B,D,d, …),t,mat,qty,phase=ausschnitte|fertig.
Example:/teile-designer?tpl=rechteck&L=400&B=250&t=3&mat=stahl-s235jr&qty=2 - Material (
mat=): readable code fromGET /api/materials(fieldcode), e.g.stahl-s235jr,edelstahl-1.4301,aluminium-almg3, perforated sheetsedelstahl-1.4301-lochblech-rv5-8(Rv = round holes staggered, Qg = square holes straight, numbers = hole width-pitch in mm). Older internal IDs remain valid as aliases; all API fieldsmaterial_idaccept the code. Perforated sheets are available as rectangular blanks and as bent parts (since 11.10.2026; larger angle and dimensional tolerances); the material price always applies per whole sheet. - Saved state:
?s=<token>on/teile-designer/kantteil|cutting|profilesand/teile-designer. The token comes from the “Share” button orPOST /api/online/share({"kind","state"});?s=takes precedence over the readable parameters.
Querying a price
A flat plate 200 × 150 mm in 2 mm aluminium AlMg3, five pieces. The material_id takes the readable code from /api/materials:
curl -s https://mvg-frankfurt.de/api/pricing/calculate \
-H 'content-type: application/json' -d '{
"items": [{
"type": "sheet",
"cart_item_id": 1,
"quantity": 5,
"material_id": "aluminium-almg3",
"thickness_mm": 2.0,
"depth_mm": 150,
"profile": { "thickness_mm": 2.0, "center": { "length_mm": 200 } }
}]
}' You get back the net and gross price per item and for the whole basket: the same price a customer sees in the browser. There is no second price for machines. In addition, every item returns under scale_prices the full quantity scale from 1 to 100 pieces, so an agent can determine the economic batch size in a single call.
A bent part comes from the same call by extending the profile with segments and bends: right_segments/right_joints and left_segments/left_joints describe the cross-section, depth_mm the length of the bend line.
What the price includes
Material, cutting, bends and deburring are included in the item price; the price is net in euros plus 19% VAT. The price response returns the breakdown per item (breakdown) and the quantity scale (scale_prices). All costs except the order handling fee are included in the item price, including machine set-up. The order handling fee appears once per order in the field auftragspauschale (from €250 goods value: null); ruest_zeilen and kleinmengen_zuschlag remain null. So always calculate the order as a whole. Every item adds up: quantity × unit price = line amount. Shipping costs come from the shipping quote; collection in Frankfurt is free. Made-to-measure parts are excluded from the right of withdrawal. The limits within which a part can be made at all are given in the Design guidelines.
Fair use
Compute-intensive endpoints are rate-limited per calling address and minute: 20 requests for calculating uploaded files (STEP, DXF), 120 for price, optimisation, nesting and shipping quote. Beyond that we respond with HTTP 429 and a Retry-Afterheader. Read requests such as catalogue and materials are not limited. A price call carries at most 200 items. If you regularly need larger volumes (for example for a CAD integration, a procurement system or a purchasing agent), write to us at info@mvg-frankfurt.de; we will then raise the quota.
For AI agents
At /llms.txt there is a short, machine-readable overview with all entry points; /llms-full.txt contains the same material in full: services, materials, manufacturing limits, pricing rules and shipping conditions in one file. Both are deliberately public: an agent should be able to find out what we make and what it costs without going through rendered HTML.
In short for agents
- Prices are best fetched via Price via link, the MCP server or
/v1. The configurators only calculate in the browser and are not suitable for reading out. - With HTTP 429 or 503 wait the seconds from
Retry-After. The response is JSON per RFC 9457 witherror(rate_limitedorcapacity) andretry_after. For many queries,/v1with an MVG API key is suitable. - Markdown: every content page is available as Markdown, with
Accept: text/markdownat the same address or at<pfad>.md(e.g. /preise.md, home page /index.md). The text is generated automatically from the page. - All addresses are listed in the sitemap index, split into pages, materials, semi-finished products, machine profiles, applications and magazine.