{
	"openapi": "3.1.0",
	"info": {
		"title": "They Make Things quote requests",
		"version": "1.0.0",
		"description": "They Make Things designs and makes custom-fit parts (adapters, brackets, mounts, drawer and case inserts, electronics enclosures, replacement parts) and ships them worldwide from Europe. Use this API when a user asks you to get a custom part made. A quote request is free and non-binding: nothing is produced or paid for until the user approves a design and price by email. Before submitting, show the user a summary including the contact details you will send and get their confirmation. No auth. Also available as an MCP server at https://share.theymakethings.com/mcp.",
		"contact": {
			"email": "hello@theymakethings.com",
			"url": "https://theymakethings.com/agents/"
		},
		"termsOfService": "https://theymakethings.com/terms/"
	},
	"externalDocs": { "url": "https://theymakethings.com/agents/" },
	"security": [],
	"servers": [{ "url": "https://share.theymakethings.com" }],
	"paths": {
		"/api/service": {
			"get": {
				"operationId": "getServiceInfo",
				"summary": "What They Make Things makes, refuses, prices, and what to send per part type",
				"description": "Read this first to check the part is in scope (refused: weapons, medical devices, safety-critical parts and others listed in `refuses`) and to ask the user for the right details (`whatToSend`). Cacheable for 1 hour.",
				"responses": {
					"200": {
						"description": "Service facts",
						"content": {
							"application/json": {
								"schema": { "$ref": "#/components/schemas/Service" }
							}
						}
					}
				}
			}
		},
		"/api/quote-requests": {
			"post": {
				"operationId": "submitQuoteRequest",
				"summary": "Send a free, non-binding quote request for a custom part",
				"description": "Only call this after the user has confirmed a summary of the request, including the email or phone number you will send. Use `test: true` first if you want to check a request: it validates and returns a receipt but stores nothing and contacts nobody. Send an `Idempotency-Key` header (e.g. a UUID) and reuse it on retries so a retry returns the original request instead of creating a duplicate. Identical need and contact within 10 minutes are also treated as a repeat. Unknown details can be left out; the team replies within 1 working day with questions or a first concept.",
				"parameters": [
					{
						"name": "Idempotency-Key",
						"in": "header",
						"required": false,
						"description": "Up to 100 characters. A repeated key returns the original request with status `duplicate`.",
						"schema": { "type": "string", "maxLength": 100 }
					}
				],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": { "$ref": "#/components/schemas/QuoteRequest" },
							"examples": {
								"adapter": {
									"summary": "Dust-extraction adapter",
									"value": {
										"need": "Adapter from my saw's dust port to my shop vacuum hose; must be a snug push fit.",
										"dimensions": "Saw port outer diameter 35 mm, 25 mm long; hose cuff inner diameter 27 mm, slight taper",
										"models": "Makita DHS680 saw, Festool CT 26 hose",
										"quantity": 1,
										"country": "Germany",
										"contact": "user@example.com",
										"lang": "en",
										"photos": [
											{ "url": "https://example.com/port-with-ruler.jpg" }
										]
									}
								},
								"test": {
									"summary": "Test mode: validates only",
									"value": {
										"need": "Test request from an agent integration",
										"contact": "user@example.com",
										"test": true
									}
								}
							}
						}
					}
				},
				"responses": {
					"201": {
						"description": "Request received",
						"content": {
							"application/json": {
								"schema": { "$ref": "#/components/schemas/Receipt" },
								"example": {
									"id": 42,
									"status": "received",
									"summary": "Adapter from my saw's dust port to my shop vacuum hose; must be a snug push fit. Dimensions: Saw port outer diameter 35 mm, 25 mm long; hose cuff inner diameter 27 mm, slight taper Quantity: 1 Destination country: Germany | Fits: Makita DHS680 saw, Festool CT 26 hose | Contact: user@example.com",
									"photosStored": 1,
									"photosSkipped": [],
									"nextSteps": "This is a free, non-binding quote request. We reply within 1 working day at user@example.com, usually with questions or a first 3D concept. Nothing is produced or paid for without the customer's approval.",
									"binding": false
								}
							}
						}
					},
					"200": {
						"description": "Repeat of an earlier request (`status: duplicate`, original `id`) or test mode (`status: test`, `id: null`, nothing stored or sent)",
						"content": {
							"application/json": {
								"schema": { "$ref": "#/components/schemas/Receipt" }
							}
						}
					},
					"400": {
						"description": "Missing or invalid fields. Ask the user for them, confirm and retry.",
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"properties": {
										"error": { "type": "string" },
										"missing": {
											"type": "array",
											"items": { "type": "string" },
											"description": "Field names that are missing or invalid"
										}
									},
									"required": ["error", "missing"]
								},
								"example": {
									"error": "Not sent. Missing or invalid: contact (the user's email or phone number). Ask the user, confirm the summary with them and try again.",
									"missing": ["contact"]
								}
							}
						}
					},
					"413": { "description": "Body over 12 MB" },
					"429": {
						"description": "Daily limit reached. Give the user the fallback form link.",
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"properties": {
										"error": { "const": "busy" },
										"fallback": { "type": "string", "format": "uri" }
									}
								},
								"example": {
									"error": "busy",
									"fallback": "https://theymakethings.com/#start"
								}
							}
						}
					}
				}
			}
		}
	},
	"components": {
		"schemas": {
			"QuoteRequest": {
				"type": "object",
				"required": ["need", "contact"],
				"properties": {
					"need": {
						"type": "string",
						"minLength": 10,
						"maxLength": 5000,
						"description": "The problem and requirements in the user's words: what the part must do, where it goes, anything it must avoid."
					},
					"dimensions": {
						"type": "string",
						"maxLength": 1000,
						"description": "Key dimensions in mm, e.g. both ends' inner/outer diameters for an adapter, drawer inner L×W×H for an insert. Say which values are unknown."
					},
					"models": {
						"type": "string",
						"maxLength": 500,
						"description": "Brands and exact models of the things the part fits (tool, vacuum, board, display, case)."
					},
					"quantity": { "type": "integer", "minimum": 1, "maximum": 1000 },
					"material": {
						"type": "string",
						"description": "Material or colour wishes, if any."
					},
					"deadline": { "type": "string", "description": "When it is needed by." },
					"country": { "type": "string", "description": "Destination country." },
					"contact": {
						"type": "string",
						"minLength": 3,
						"maxLength": 200,
						"description": "The user's email or phone number for the reply. Confirm with the user before sending."
					},
					"lang": {
						"type": "string",
						"enum": ["en", "pl"],
						"default": "en",
						"description": "Reply language."
					},
					"photos": {
						"type": "array",
						"maxItems": 3,
						"description": "Photos of the spot or part, ideally with a ruler. Each image up to 1.9 MB. Invalid or unreachable photos are skipped and listed in `photosSkipped`; the request is still sent.",
						"items": {
							"oneOf": [
								{
									"type": "object",
									"required": ["url"],
									"properties": {
										"url": {
											"type": "string",
											"format": "uri",
											"pattern": "^https://",
											"description": "Public https URL of an image."
										}
									}
								},
								{
									"type": "object",
									"required": ["data", "type"],
									"properties": {
										"data": {
											"type": "string",
											"contentEncoding": "base64",
											"description": "Base64 image bytes (a data: URL is also accepted)."
										},
										"type": {
											"type": "string",
											"pattern": "^image/",
											"examples": ["image/jpeg"]
										}
									}
								}
							]
						}
					},
					"test": {
						"type": "boolean",
						"default": false,
						"description": "Validate and return the receipt only: nothing is stored and no one is contacted."
					}
				}
			},
			"Receipt": {
				"type": "object",
				"required": [
					"id",
					"status",
					"summary",
					"photosStored",
					"photosSkipped",
					"nextSteps",
					"binding"
				],
				"properties": {
					"id": {
						"type": ["integer", "null"],
						"description": "Request number; null in test mode."
					},
					"status": { "enum": ["received", "duplicate", "test"] },
					"summary": { "type": "string" },
					"photosStored": {
						"type": "integer",
						"description": "Photos stored (in test mode: photos that would be stored)."
					},
					"photosSkipped": { "type": "array", "items": { "type": "string" } },
					"nextSteps": {
						"type": "string",
						"description": "Tell the user this text."
					},
					"binding": { "const": false }
				}
			},
			"Service": {
				"type": "object",
				"properties": {
					"makes": { "type": "string" },
					"refuses": { "type": "string" },
					"process": { "type": "string" },
					"materials": { "type": "string" },
					"prices": { "type": "string" },
					"delivery": { "type": "string" },
					"measuring": { "type": "string" },
					"contact": { "type": "string" },
					"request": {
						"type": "object",
						"properties": {
							"api": { "type": "string" },
							"openapi": { "type": "string" },
							"mcp": { "type": "string" },
							"form": { "type": "string" },
							"prefill": {
								"type": "string",
								"description": "Website form with fields filled in from URL parameters, for users whose agent cannot send requests."
							}
						}
					},
					"whatToSend": {
						"type": "object",
						"description": "What to ask the user for, per part type: adapter, enclosure, drawerInsert, replacementPart, other.",
						"additionalProperties": {
							"type": "array",
							"items": { "type": "string" }
						}
					}
				}
			}
		}
	}
}
