{
  "openapi": "3.1.0",
  "info": {
    "title": "PartFinder API",
    "version": "1.0.0",
    "license": {
      "name": "Proprietary",
      "url": "https://partfinder.dev/terms"
    },
    "description": "Supply-chain intelligence for industrial parts: risk scoring, supply availability, datasheets, disruption events, BOM analysis, alternates, part search and part comparison. Responses are structured for an AI agent to read — every score carries its scale, direction and an interpretation, every measure carries its unit, every successful response carries `as_of`, and every error carries an `error` code, usually with a `summary` you can relay, except a 413 (request body too large), which has no body."
  },
  "servers": [
    {
      "url": "https://api.partfinder.dev"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Authentication",
      "description": "Confirm an API key works."
    },
    {
      "name": "Parts",
      "description": "Research one part: risk, supply, datasheet, events, alternates."
    },
    {
      "name": "BOMs",
      "description": "Analyze a bill of materials line by line."
    },
    {
      "name": "Events",
      "description": "Supply-chain disruptions: events, the countries they hit, and port and sea-route activity."
    },
    {
      "name": "Search",
      "description": "Find parts for a part number or a description, with specifications and stock."
    },
    {
      "name": "Compare",
      "description": "Put parts side by side on their specifications, with a verdict per specification."
    },
    {
      "name": "RFQs",
      "description": "Send requests for quote to suppliers and read their replies."
    },
    {
      "name": "Keys",
      "description": "Give each of your own customers a key that sees only its own data."
    },
    {
      "name": "Webhooks",
      "description": "Be told when a supplier replies, or a search or a comparison ends, instead of polling."
    }
  ],
  "paths": {
    "/v1/ping": {
      "get": {
        "operationId": "ping",
        "tags": [
          "Authentication"
        ],
        "summary": "Verify an API key",
        "description": "Returns the organization the key belongs to. Use it to confirm an integration is wired correctly before calling an endpoint that does real work.",
        "responses": {
          "200": {
            "description": "The key is valid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Pong"
                },
                "example": {
                  "as_of": "2026-09-24T11:05:56.899Z",
                  "summary": "Authenticated. The PartFinder API is reachable and this key is valid.",
                  "notice": "Informational only. Risk scores, stock levels, lead times and alternate-part suggestions may be incomplete, delayed or inaccurate. Independently verify before making procurement, engineering, compliance, safety or sourcing decisions.",
                  "authenticated": {
                    "organization_id": "org_2b7c...",
                    "key_id": "ak_9f31..."
                  }
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, revoked or expired key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The caller signed in through an AI app without the permission this call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the error is `insufficient_scope`. Or the AI app was disconnected in the console; the error is `app_disconnected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "429": {
            "description": "Your organization has used its calls for this minute or this day. Wait the number of seconds in `Retry-After`, then retry.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the limit resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/parts": {
      "get": {
        "operationId": "researchPart",
        "tags": [
          "Parts"
        ],
        "summary": "Research a part",
        "description": "Supply-chain intelligence for one industrial part: a composite risk score with its four drivers, current distributor availability, a datasheet link, the live disruption events that touch it, alternate parts, and recommended actions.\n\nA cold part takes tens of seconds because it gathers live data. The result is kept server-side for 48 hours, so repeat calls return quickly; responses themselves are not HTTP-cacheable and carry `Cache-Control: no-store`.",
        "parameters": [
          {
            "name": "mpn",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "TPS54331DR",
            "description": "The manufacturer part number."
          },
          {
            "name": "manufacturer",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "example": "Texas Instruments",
            "description": "Only needed for a part number several manufacturers make, such as NE555N. Omit it otherwise — a bare part number is the normal case.\n\nIt narrows the supply figures to that manufacturer and, when known, the manufacturing origin to where that manufacturer makes the part, and with it the risk drivers that depend on where the part is made. A manufacturer that does not list the part returns a report with no supply data."
          },
          {
            "name": "country",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z]{2}$"
            },
            "example": "SG",
            "description": "The buyer's country, as a two-letter ISO 3166-1 code. Adds `supply.nearby`: the distributors holding stock in that country, then elsewhere in its region. The risk score and every other field are the same with or without it."
          }
        ],
        "responses": {
          "200": {
            "description": "The part report.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartReport"
                },
                "example": {
                  "as_of": "2026-10-01T12:19:19.099Z",
                  "summary": "STM32F103RBT6 carries a supply-chain risk score of 33 out of 100 (medium). Flagged: manufacturing geography (medium), lead-time volatility (critical). 348,529 units are held across 28 distributors.",
                  "notice": "Informational only. Risk scores, stock levels, lead times and alternate-part suggestions may be incomplete, delayed or inaccurate. Independently verify before making procurement, engineering, compliance, safety or sourcing decisions.",
                  "notes": [],
                  "part": {
                    "mpn": "STM32F103RBT6",
                    "manufacturer": "STMicroelectronics",
                    "category": "ARM Cortex-M3 MCU",
                    "lifecycle": "Active",
                    "manufacturing_country": "Malaysia",
                    "manufacturing_country_code": "MY"
                  },
                  "risk": {
                    "composite": {
                      "value": 33,
                      "scale": 100,
                      "direction": "higher_is_worse",
                      "interpretation": "Medium. Combined from manufacturing geography, lead-time volatility, live disruption exposure and how hard this part is to replace."
                    },
                    "tier": "medium",
                    "drivers": {
                      "geography": {
                        "tier": "medium",
                        "interpretation": "Medium. Reflects the risk profile of Malaysia."
                      },
                      "lead_time_volatility": {
                        "tier": "critical",
                        "interpretation": "Critical. Lead times for this part are highly unstable."
                      },
                      "disruption_exposure": {
                        "tier": "low",
                        "interpretation": "Low. Little exposure to active disruptions. 5 events and shipping conditions are listed in `disruption_events`."
                      },
                      "replacement_difficulty": {
                        "tier": "low",
                        "interpretation": "Low. Easy to source from several places."
                      }
                    },
                    "why_at_risk": "Made in Malaysia. 348,529 units in stock across 28 distributors. The middle half of quoted lead times runs 52 to 364 days. 5 disruptions and shipping conditions are listed for this part. No alternates are listed.",
                    "drivers_detail": [
                      {
                        "title": "Lead time",
                        "description": "The middle half of quoted lead times runs 52 to 364 days, with a median of 220 days."
                      },
                      {
                        "title": "Disruptions",
                        "description": "5 are listed for this part, including: Red Sea / Suez ship transits 54% below the usual level for this time of year; Wacker Chemie may close Tennessee polysilicon plant amid U.S. trade measures."
                      }
                    ]
                  },
                  "supply": {
                    "stock": {
                      "value": 348529,
                      "unit": "units"
                    },
                    "lead_time_median": {
                      "value": 220,
                      "unit": "days"
                    },
                    "lead_time_min": {
                      "value": 52,
                      "unit": "days"
                    },
                    "lead_time_max": {
                      "value": 364,
                      "unit": "days"
                    },
                    "distributor_count": 28,
                    "distributors": [
                      {
                        "name": "Win Source",
                        "country": "United Kingdom",
                        "country_code": "GB",
                        "regions": [
                          "Europe"
                        ],
                        "subregion": "Northern Europe",
                        "stock": {
                          "value": 91714,
                          "unit": "units"
                        }
                      },
                      {
                        "name": "Farnell",
                        "country": "United Kingdom",
                        "country_code": "GB",
                        "regions": [
                          "Europe"
                        ],
                        "subregion": "Northern Europe",
                        "stock": {
                          "value": 63767,
                          "unit": "units"
                        }
                      }
                    ],
                    "nearby": null
                  },
                  "datasheet": {
                    "url": "https://apexcomponent.com/wp-content/uploads/2026/05/STM32F103RBT6-STMicroelectronics-Datasheet.pdf",
                    "source": "other"
                  },
                  "disruption_events": [
                    {
                      "kind": "sea_route",
                      "id": null,
                      "title": "Red Sea / Suez: ship transits 54% below the usual level for this time of year",
                      "root_cause": null,
                      "country": null,
                      "severity": null,
                      "published_date": "2026-09-20T00:00:00.000Z",
                      "url": null
                    },
                    {
                      "kind": "news",
                      "id": 1179,
                      "title": "Wacker Chemie may close Tennessee polysilicon plant amid U.S. trade measures",
                      "root_cause": "geopolitical_action",
                      "country": "United States",
                      "severity": "high",
                      "published_date": "2026-09-04T00:00:00.000Z",
                      "url": "https://www.reuters.com/business/trumps-bid-shield-chip-supply-chain-could-backfire-tennessee-2026-09-04/"
                    },
                    {
                      "kind": "news",
                      "id": 1178,
                      "title": "Chinese rare-earth firms withhold U.S. shipments amid Beijing pressure",
                      "root_cause": "geopolitical_action",
                      "country": "China",
                      "severity": "high",
                      "published_date": "2026-09-04T00:00:00.000Z",
                      "url": "https://www.usnews.com/news/top-news/articles/2026-09-04/china-rare-earth-firms-halt-some-us-shipments-over-geopolitical-worries-sources-say"
                    },
                    {
                      "kind": "news",
                      "id": 1189,
                      "title": "China imposes provisional duties on Japanese semiconductor chemical",
                      "root_cause": "regulatory_change",
                      "country": "China",
                      "severity": "medium",
                      "published_date": "2026-09-09T00:00:00.000Z",
                      "url": "https://hk.epochtimes.com/news/2026-09-09/80403878"
                    },
                    {
                      "kind": "news",
                      "id": 1166,
                      "title": "China imposes helium export controls affecting global high‑tech supply",
                      "root_cause": "regulatory_change",
                      "country": "China",
                      "severity": "high",
                      "published_date": "2026-07-11T00:00:00.000Z",
                      "url": "https://www.caixinglobal.com/2026-07-11/china-bans-helium-exports-as-global-supply-crunch-hits-chipmaking-gas-102462943.html"
                    }
                  ],
                  "alternates": [],
                  "recommended_actions": [
                    "For factory orders, allow for the 220-day median quoted lead time.",
                    "Monitor Red Sea / Suez ship transits 54% below the usual level for this time of year."
                  ]
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "400": {
            "description": "The `mpn` parameter is missing, or `country` is not a country code.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, revoked or expired key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "This request was not run and nothing was charged. The error code names the case. `credits_exhausted`: your plan's credits for this month are used up; upgrade the plan in the PartFinder console. `payment_method_required`: your organization is on pay as you go with no payment method; add one in the console, or choose a plan. `payment_failed`: pay as you go is paused because the last payment failed; update the payment method in the console. `usage_limit_reached`: your organization, on pay as you go, has reached its limit for unpaid usage; calls resume once the current payment goes through. With a key made with `POST /v1/keys`, the `summary` tells your customer to contact your organization, and the answer carries no `billing_url`. The `summary` can be relayed as is; retry once the case is resolved.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "credits_exhausted",
                  "summary": "Your organization's credits for this month are used up, so this request was not run. Upgrade your plan in the PartFinder console at https://console.partfinder.dev/settings/billing, then try again.",
                  "reason": "This request needs 20 credits and 0 are left.",
                  "billing_url": "https://console.partfinder.dev/settings/billing"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "403": {
            "description": "The caller signed in through an AI app without the permission this call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the error is `insufficient_scope`. Or the AI app was disconnected in the console; the error is `app_disconnected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "404": {
            "description": "No distributor catalogue lists that part number exactly. Usually a typo or a partial part number. No report is produced — we do not generate one for a part we cannot find. When listed part numbers begin with what you sent, `did_you_mean` carries up to five of them.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "422": {
            "description": "The input does not identify one specific part — a question, a category, or a generic specification. When it described a class of parts, `did_you_mean` carries real part numbers to retry with.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "429": {
            "description": "Your organization has used its calls for this minute or this day. Wait the number of seconds in `Retry-After`, then retry.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the limit resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The report could not be produced right now. Retry shortly.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "503": {
            "description": "We could not confirm the part number is real, because the supplier data needed to check it is momentarily unavailable. No report is produced for a part that cannot be verified. Temporary — retry shortly.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          }
        }
      }
    },
    "/v1/alternates": {
      "get": {
        "operationId": "getAlternates",
        "tags": [
          "Parts"
        ],
        "summary": "Find alternate parts",
        "description": "Parts that could replace one part, up to five, each with its current stock, distributor count, lead time and lifecycle. Never the part itself or the same part in another package.\n\nThe list is kept server-side for 48 hours, so repeat calls return quickly; stock is read fresh on every call. Responses carry `Cache-Control: no-store`. Confirm fit and specifications against the datasheets before substituting.",
        "parameters": [
          {
            "name": "mpn",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "TPS54331DR",
            "description": "The manufacturer part number to find alternates for."
          },
          {
            "name": "manufacturer",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "example": "Texas Instruments",
            "description": "Only needed for a part number several manufacturers make, such as NE555N. Omit it otherwise."
          }
        ],
        "responses": {
          "200": {
            "description": "The alternates. The list can be empty.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AlternatesReport"
                },
                "example": {
                  "as_of": "2026-09-29T07:28:59.267Z",
                  "summary": "3 alternates for TPS54331DR: AP63301WU-7 (Diodes Incorporated), AP63300WU-7 (Diodes Incorporated), AP64351SP-13 (Diodes Incorporated). 3 of them are in stock at a distributor. Confirm fit against the datasheets before substituting.",
                  "notice": "Informational only. Risk scores, stock levels, lead times and alternate-part suggestions may be incomplete, delayed or inaccurate. Independently verify before making procurement, engineering, compliance, safety or sourcing decisions.",
                  "part": {
                    "mpn": "TPS54331DR",
                    "manufacturer": null
                  },
                  "alternates": [
                    {
                      "mpn": "AP63301WU-7",
                      "manufacturer": "Diodes Incorporated",
                      "description": "Adjustable 3 A step-down regulator, 6-pin package",
                      "match": "similar",
                      "lifecycle": "Active",
                      "supply": {
                        "stock": {
                          "value": 81702,
                          "unit": "units"
                        },
                        "distributor_count": 3,
                        "lead_time_median": {
                          "value": 101,
                          "unit": "days"
                        }
                      }
                    },
                    {
                      "mpn": "AP63300WU-7",
                      "manufacturer": "Diodes Incorporated",
                      "description": "Adjustable 3 A step-down regulator, 6-pin package",
                      "match": "similar",
                      "lifecycle": "Active",
                      "supply": {
                        "stock": {
                          "value": 54602,
                          "unit": "units"
                        },
                        "distributor_count": 4,
                        "lead_time_median": {
                          "value": 129,
                          "unit": "days"
                        }
                      }
                    },
                    {
                      "mpn": "AP64351SP-13",
                      "manufacturer": "Diodes Incorporated",
                      "description": "Adjustable 3.5 A step-down regulator, 8-pin package",
                      "match": "similar",
                      "lifecycle": "Active",
                      "supply": {
                        "stock": {
                          "value": 78939,
                          "unit": "units"
                        },
                        "distributor_count": 6,
                        "lead_time_median": {
                          "value": 84,
                          "unit": "days"
                        }
                      }
                    }
                  ]
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "400": {
            "description": "The `mpn` parameter is missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, revoked or expired key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "This request was not run and nothing was charged. The error code names the case. `credits_exhausted`: your plan's credits for this month are used up; upgrade the plan in the PartFinder console. `payment_method_required`: your organization is on pay as you go with no payment method; add one in the console, or choose a plan. `payment_failed`: pay as you go is paused because the last payment failed; update the payment method in the console. `usage_limit_reached`: your organization, on pay as you go, has reached its limit for unpaid usage; calls resume once the current payment goes through. With a key made with `POST /v1/keys`, the `summary` tells your customer to contact your organization, and the answer carries no `billing_url`. The `summary` can be relayed as is; retry once the case is resolved.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "credits_exhausted",
                  "summary": "Your organization's credits for this month are used up, so this request was not run. Upgrade your plan in the PartFinder console at https://console.partfinder.dev/settings/billing, then try again.",
                  "reason": "This request needs 1 credit and 0 are left.",
                  "billing_url": "https://console.partfinder.dev/settings/billing"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "403": {
            "description": "The caller signed in through an AI app without the permission this call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the error is `insufficient_scope`. Or the AI app was disconnected in the console; the error is `app_disconnected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "404": {
            "description": "No distributor catalogue lists that part number exactly. Usually a typo or a partial part number. When listed part numbers begin with what you sent, `did_you_mean` carries up to five of them.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "422": {
            "description": "The input does not identify one specific part. When it described a class of parts, `did_you_mean` carries real part numbers to retry with.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "429": {
            "description": "Your organization has used its calls for this minute or this day. Wait the number of seconds in `Retry-After`, then retry.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the limit resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The alternates could not be produced right now. Retry shortly.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "503": {
            "description": "We could not confirm the part number is real right now. No list is produced for a part that cannot be verified. Temporary — retry shortly.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          }
        }
      }
    },
    "/v1/bom": {
      "post": {
        "operationId": "analyzeBom",
        "tags": [
          "BOMs"
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "example": "6f1c2e9a-3b7d-4c55-9a0e-2d8f41b7c3e1",
            "description": "Set by your client code, not by a person or a model: generate a new value (a UUID) for each submission, and send the same value again only when retrying that same request after a timeout. A retry with the same key returns the BOM the first request created instead of a duplicate. A key is recognised only from the same API key that sent it. The body is not compared, so never reuse a key for a different or updated BOM. A key is remembered for as long as the BOM it created exists; after that BOM is deleted, the same key starts a new one. Optional."
          }
        ],
        "summary": "Analyze a BOM",
        "description": "Submit a bill of materials for supply-chain risk analysis. Use it whenever your user asks to scan or analyze a BOM or a list of parts, even a list of one line. Every line is identified, its origin and current distributor availability resolved, and a risk score computed from manufacturing geography, live disruption exposure, lead-time volatility and sourcing gaps across the BOM.\n\nThe work runs in the background: this call answers `202` at once with the BOM id, and `GET /v1/bom/{id}` carries the report, partial while `status` is `processing` and full once it is `complete`. Time to complete varies with the parts in the BOM. One call carries up to 500 lines; a BOM is kept until you delete it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BomRequest"
              },
              "example": {
                "name": "Controller board rev C",
                "parts": [
                  {
                    "mpn": "TPS54331DR",
                    "manufacturer": "Texas Instruments",
                    "quantity": 100,
                    "supplier": "Mouser"
                  },
                  {
                    "mpn": "STM32F407VGT6",
                    "quantity": 50
                  },
                  {
                    "mpn": "6305-2RS",
                    "manufacturer": "SKF",
                    "quantity": 10,
                    "description": "Deep groove ball bearing"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted. Analysis has started.",
            "headers": {
              "Location": {
                "description": "The absolute URL of the report, `GET /v1/bom/{id}` on the server this request reached.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait before polling the report. Always 5.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BomAccepted"
                },
                "example": {
                  "as_of": "2026-09-28T15:49:42.444Z",
                  "summary": "Accepted 3 lines. The BOM is being analyzed; poll GET /v1/bom/fc2b3371-7eb7-48cd-8847-70b2d76b0be8 until status is \"complete\".",
                  "notice": "Informational only. Risk scores, stock levels, lead times and alternate-part suggestions may be incomplete, delayed or inaccurate. Independently verify before making procurement, engineering, compliance, safety or sourcing decisions.",
                  "bom": {
                    "id": "fc2b3371-7eb7-48cd-8847-70b2d76b0be8",
                    "name": "Controller board rev C",
                    "status": "processing",
                    "lines": 3,
                    "created_at": "2026-09-28T15:49:38.809Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The body is not valid: no `parts`, a line without `mpn`, or a bad quantity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, revoked or expired key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "This request was not run and nothing was charged. The error code names the case. `credits_exhausted`: your plan's credits for this month are used up; upgrade the plan in the PartFinder console. `payment_method_required`: your organization is on pay as you go with no payment method; add one in the console, or choose a plan. `payment_failed`: pay as you go is paused because the last payment failed; update the payment method in the console. `usage_limit_reached`: your organization, on pay as you go, has reached its limit for unpaid usage; calls resume once the current payment goes through. With a key made with `POST /v1/keys`, the `summary` tells your customer to contact your organization, and the answer carries no `billing_url`. The `summary` can be relayed as is; retry once the case is resolved.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "credits_exhausted",
                  "summary": "Your organization's credits for this month are used up, so this request was not run. Upgrade your plan in the PartFinder console at https://console.partfinder.dev/settings/billing, then try again.",
                  "reason": "This request needs 75 credits and 0 are left.",
                  "billing_url": "https://console.partfinder.dev/settings/billing"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "403": {
            "description": "The caller signed in through an AI app without the permission this call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the error is `insufficient_scope`. Or the AI app was disconnected in the console; the error is `app_disconnected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "422": {
            "description": "More than 500 lines in one call. `reason` states the limit and what was sent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "429": {
            "description": "Your organization has used its calls for this minute or this day. Wait the number of seconds in `Retry-After`, then retry.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the limit resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The analysis could not be started. Nothing was stored; retry shortly.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listBoms",
        "tags": [
          "BOMs"
        ],
        "summary": "List your BOMs",
        "description": "Every BOM your organization has submitted (with a key made with `POST /v1/keys`, only that customer's), newest first, with its status, one page at a time. When `next_cursor` is not null, call again with `cursor` set to it for the next page.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            },
            "description": "BOMs per page, 1 to 100. Defaults to 50."
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "`next_cursor` from the previous page, unchanged. Omit it for the first page."
          }
        ],
        "responses": {
          "200": {
            "description": "The list.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BomList"
                },
                "example": {
                  "as_of": "2026-09-26T06:48:31.721Z",
                  "summary": "2 BOMs, newest first, 0 still processing. More remain: call GET /v1/bom again with cursor set to next_cursor.",
                  "notice": "Informational only. Risk scores, stock levels, lead times and alternate-part suggestions may be incomplete, delayed or inaccurate. Independently verify before making procurement, engineering, compliance, safety or sourcing decisions.",
                  "boms": [
                    {
                      "id": "7df7d0aa-ba87-4a66-bd6d-01c033bafe4e",
                      "name": "Controller board rev C",
                      "status": "complete",
                      "lines": 3,
                      "created_at": "2026-09-26T06:48:09.659Z",
                      "completed_at": "2026-09-26T06:48:26.585Z"
                    },
                    {
                      "id": "181eff86-1651-4b9c-9344-d5c5a1c4a409",
                      "name": "Scan BOM",
                      "status": "complete",
                      "lines": 2,
                      "created_at": "2026-09-24T07:31:51.637Z",
                      "completed_at": "2026-09-24T07:32:20.462Z"
                    }
                  ],
                  "next_cursor": "MTgxZWZmODYtMTY1MS00YjljLTkzNDQtZDVjNWExYzRhNDA5"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "400": {
            "description": "`limit` is outside 1-100, or `cursor` is not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, revoked or expired key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The caller signed in through an AI app without the permission this call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the error is `insufficient_scope`. Or the AI app was disconnected in the console; the error is `app_disconnected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "429": {
            "description": "Your organization has used its calls for this minute or this day. Wait the number of seconds in `Retry-After`, then retry.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the limit resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/bom/upload": {
      "post": {
        "operationId": "uploadBom",
        "tags": [
          "BOMs"
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "example": "6f1c2e9a-3b7d-4c55-9a0e-2d8f41b7c3e1",
            "description": "Set by your client code, not by a person or a model: generate a new value (a UUID) for each submission, and send the same value again only when retrying that same request after a timeout. A retry with the same key returns the BOM the first request created instead of a duplicate. A key is recognised only from the same API key that sent it. The body is not compared, so never reuse a key for a different or updated BOM. A key is remembered for as long as the BOM it created exists; after that BOM is deleted, the same key starts a new one. Optional."
          }
        ],
        "summary": "Upload a BOM file",
        "description": "Submit a bill of materials as a CSV or XLSX file. We find the part-number column and the other columns we recognise (manufacturer, quantity, supplier, description, and more), then run the same analysis as `POST /v1/bom`: the call answers `202` with the BOM id and `GET /v1/bom/{id}` carries the report. One file carries up to 2,000 rows. A request body over 4.5 MB is refused with `413`.",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "The BOM, as .csv or .xlsx."
                  },
                  "name": {
                    "type": "string",
                    "description": "A label for this BOM. Defaults to the file name."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted. Analysis has started.",
            "headers": {
              "Location": {
                "description": "The absolute URL of the report, `GET /v1/bom/{id}` on the server this request reached.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait before polling the report. Always 5.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BomAccepted"
                },
                "example": {
                  "as_of": "2026-09-28T15:49:42.444Z",
                  "summary": "Accepted 3 lines. The BOM is being analyzed; poll GET /v1/bom/fc2b3371-7eb7-48cd-8847-70b2d76b0be8 until status is \"complete\".",
                  "notice": "Informational only. Risk scores, stock levels, lead times and alternate-part suggestions may be incomplete, delayed or inaccurate. Independently verify before making procurement, engineering, compliance, safety or sourcing decisions.",
                  "bom": {
                    "id": "fc2b3371-7eb7-48cd-8847-70b2d76b0be8",
                    "name": "Controller board rev C",
                    "status": "processing",
                    "lines": 3,
                    "created_at": "2026-09-28T15:49:38.809Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "No file, an unsupported file type, or an empty or unreadable file.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, revoked or expired key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "This request was not run and nothing was charged. The error code names the case. `credits_exhausted`: your plan's credits for this month are used up; upgrade the plan in the PartFinder console. `payment_method_required`: your organization is on pay as you go with no payment method; add one in the console, or choose a plan. `payment_failed`: pay as you go is paused because the last payment failed; update the payment method in the console. `usage_limit_reached`: your organization, on pay as you go, has reached its limit for unpaid usage; calls resume once the current payment goes through. With a key made with `POST /v1/keys`, the `summary` tells your customer to contact your organization, and the answer carries no `billing_url`. The `summary` can be relayed as is; retry once the case is resolved.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "credits_exhausted",
                  "summary": "Your organization's credits for this month are used up, so this request was not run. Upgrade your plan in the PartFinder console at https://console.partfinder.dev/settings/billing, then try again.",
                  "reason": "This request needs 75 credits and 0 are left.",
                  "billing_url": "https://console.partfinder.dev/settings/billing"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "403": {
            "description": "The caller signed in through an AI app without the permission this call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the error is `insufficient_scope`. Or the AI app was disconnected in the console; the error is `app_disconnected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "413": {
            "description": "The request body is over 4.5 MB. This response does not carry the usual error object."
          },
          "422": {
            "description": "More than 2,000 rows in one file (`bom_too_large`), or no column we can identify as part numbers (`bom_columns_not_found`, with the headers seen in `reason`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "429": {
            "description": "Your organization has used its calls for this minute or this day. Wait the number of seconds in `Retry-After`, then retry.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the limit resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The analysis could not be started. Nothing was stored; retry shortly.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          }
        }
      }
    },
    "/v1/bom/{id}": {
      "get": {
        "operationId": "getBom",
        "tags": [
          "BOMs"
        ],
        "summary": "Get a BOM report",
        "description": "The report for one BOM: a risk average and distribution, the gaps across the BOM, where it is made, and every line with its own score, drivers and current supply. While `bom.status` is `processing` the lines already analyzed are scored and the rest are `pending`; poll until it is `complete`. Each poll counts toward your rate limit. Scores are computed at read time against current disruption events and distributor figures.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The BOM id returned by `POST /v1/bom`."
          },
          {
            "name": "country",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z]{2}$"
            },
            "example": "SG",
            "description": "The buyer's country, as a two-letter ISO 3166-1 code. The scores are computed for a buyer there, and `scored_for_country` names it. Without it the scores are not tied to any buyer location."
          }
        ],
        "responses": {
          "200": {
            "description": "The BOM report.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BomReport"
                },
                "example": {
                  "as_of": "2026-10-01T12:45:25.731Z",
                  "summary": "3 lines: 0 critical, 0 high, 2 medium, 0 low, 1 not scored. Flagged at medium or above: geography on 3 lines, disruption exposure on 1 line, lead-time volatility on 2 lines.",
                  "notice": "Informational only. Risk scores, stock levels, lead times and alternate-part suggestions may be incomplete, delayed or inaccurate. Independently verify before making procurement, engineering, compliance, safety or sourcing decisions.",
                  "bom": {
                    "id": "fc2b3371-7eb7-48cd-8847-70b2d76b0be8",
                    "name": "Controller board rev C",
                    "status": "complete",
                    "lines": 3,
                    "created_at": "2026-09-28T15:49:38.809Z",
                    "completed_at": "2026-09-28T15:50:00.022Z",
                    "identified": 3,
                    "unresolved": 0
                  },
                  "scored_for_country": null,
                  "risk": {
                    "average": {
                      "value": 29,
                      "scale": 100,
                      "direction": "higher_is_worse",
                      "interpretation": "Medium. The mean of the 2 scored lines."
                    },
                    "distribution": {
                      "critical": 0,
                      "high": 0,
                      "medium": 2,
                      "low": 0,
                      "not_scored": 1
                    },
                    "gaps": [
                      {
                        "kind": "no_supplier",
                        "lines": 2,
                        "interpretation": "2 lines name no supplier. A data gap; it counts in the gaps driver."
                      }
                    ]
                  },
                  "countries": [
                    {
                      "country": "Mexico",
                      "country_code": "MX",
                      "lines": 1,
                      "share_percent": 33.3,
                      "worst_tier": "medium"
                    },
                    {
                      "country": "Malaysia",
                      "country_code": "MY",
                      "lines": 1,
                      "share_percent": 33.3,
                      "worst_tier": "medium"
                    },
                    {
                      "country": "China",
                      "country_code": "CN",
                      "lines": 1,
                      "share_percent": 33.3,
                      "worst_tier": null
                    }
                  ],
                  "parts": [
                    {
                      "line": 1,
                      "mpn": "TPS54331DR",
                      "manufacturer": "Texas Instruments",
                      "quantity": 100,
                      "status": "resolved",
                      "notes": [],
                      "part": {
                        "identified_mpn": null,
                        "category": "Semiconductors",
                        "lifecycle": "Active",
                        "manufacturing_country": "Mexico",
                        "manufacturing_country_code": "MX"
                      },
                      "risk": {
                        "composite": {
                          "value": 31,
                          "scale": 100,
                          "direction": "higher_is_worse",
                          "interpretation": "Medium. Combined from manufacturing geography, disruption exposure, lead-time volatility and sourcing gaps."
                        },
                        "tier": "medium",
                        "drivers": {
                          "geography": {
                            "tier": "high",
                            "interpretation": "High. Reflects the risk profile of Mexico."
                          },
                          "disruption_exposure": {
                            "tier": "medium",
                            "interpretation": "Medium. Some exposure to active disruptions. 5 events and shipping conditions are listed in `events`."
                          },
                          "lead_time_volatility": {
                            "tier": "medium",
                            "interpretation": "Medium. Lead times for this part vary somewhat."
                          },
                          "gaps": {
                            "tier": "low",
                            "interpretation": "Low. No sourcing gap flagged for this line."
                          }
                        },
                        "gap_flags": [],
                        "events": [
                          {
                            "kind": "port_activity",
                            "id": null,
                            "title": "Ports in Mexico: ship arrivals 21% below the usual level for this time of year",
                            "root_cause": null,
                            "country": "Mexico",
                            "severity": null,
                            "published_date": "2026-09-18T00:00:00.000Z",
                            "url": null
                          },
                          {
                            "kind": "news",
                            "id": 1179,
                            "title": "Wacker Chemie may close Tennessee polysilicon plant amid U.S. trade measures",
                            "root_cause": "geopolitical_action",
                            "country": "United States",
                            "severity": "high",
                            "published_date": "2026-09-04T00:00:00.000Z",
                            "url": "https://www.reuters.com/business/trumps-bid-shield-chip-supply-chain-could-backfire-tennessee-2026-09-04/"
                          },
                          {
                            "kind": "sea_route",
                            "id": null,
                            "title": "Panama Canal: ship transits 21% below the usual level for this time of year",
                            "root_cause": null,
                            "country": null,
                            "severity": null,
                            "published_date": "2026-09-20T00:00:00.000Z",
                            "url": null
                          },
                          {
                            "kind": "news",
                            "id": 1189,
                            "title": "China imposes provisional duties on Japanese semiconductor chemical",
                            "root_cause": "regulatory_change",
                            "country": "China",
                            "severity": "medium",
                            "published_date": "2026-09-09T00:00:00.000Z",
                            "url": "https://hk.epochtimes.com/news/2026-09-09/80403878"
                          },
                          {
                            "kind": "news",
                            "id": 1166,
                            "title": "China imposes helium export controls affecting global high‑tech supply",
                            "root_cause": "regulatory_change",
                            "country": "China",
                            "severity": "high",
                            "published_date": "2026-07-11T00:00:00.000Z",
                            "url": "https://www.caixinglobal.com/2026-07-11/china-bans-helium-exports-as-global-supply-crunch-hits-chipmaking-gas-102462943.html"
                          }
                        ]
                      },
                      "supply": {
                        "stock": {
                          "value": 9215,
                          "unit": "units"
                        },
                        "lead_time_median": {
                          "value": 94,
                          "unit": "days"
                        },
                        "lead_time_min": {
                          "value": 84,
                          "unit": "days"
                        },
                        "lead_time_max": {
                          "value": 106,
                          "unit": "days"
                        },
                        "distributor_count": 2,
                        "scope": "manufacturer",
                        "distributors": [
                          {
                            "name": "Mouser",
                            "country": "United States",
                            "country_code": "US",
                            "regions": [
                              "Americas"
                            ],
                            "subregion": "Northern America",
                            "stock": {
                              "value": 5941,
                              "unit": "units"
                            }
                          },
                          {
                            "name": "Farnell",
                            "country": "United Kingdom",
                            "country_code": "GB",
                            "regions": [
                              "Europe"
                            ],
                            "subregion": "Northern Europe",
                            "stock": {
                              "value": 3274,
                              "unit": "units"
                            }
                          }
                        ]
                      }
                    },
                    {
                      "line": 2,
                      "mpn": "STM32F407VGT6",
                      "manufacturer": "STMicroelectronics",
                      "quantity": 50,
                      "status": "resolved",
                      "notes": [],
                      "part": {
                        "identified_mpn": null,
                        "category": "Semiconductors",
                        "lifecycle": "Active",
                        "manufacturing_country": "Malaysia",
                        "manufacturing_country_code": "MY"
                      },
                      "risk": {
                        "composite": {
                          "value": 26,
                          "scale": 100,
                          "direction": "higher_is_worse",
                          "interpretation": "Medium. Combined from manufacturing geography, disruption exposure, lead-time volatility and sourcing gaps."
                        },
                        "tier": "medium",
                        "drivers": {
                          "geography": {
                            "tier": "medium",
                            "interpretation": "Medium. Reflects the risk profile of Malaysia."
                          },
                          "disruption_exposure": {
                            "tier": "low",
                            "interpretation": "Low. Little exposure to active disruptions. 4 events and shipping conditions are listed in `events`."
                          },
                          "lead_time_volatility": {
                            "tier": "high",
                            "interpretation": "High. Lead times for this part vary a lot."
                          },
                          "gaps": {
                            "tier": "low",
                            "interpretation": "Low. Sourcing gaps on this line: no supplier named."
                          }
                        },
                        "gap_flags": [
                          "no_supplier"
                        ],
                        "events": [
                          {
                            "kind": "sea_route",
                            "id": null,
                            "title": "Red Sea / Suez: ship transits 54% below the usual level for this time of year",
                            "root_cause": null,
                            "country": null,
                            "severity": null,
                            "published_date": "2026-09-20T00:00:00.000Z",
                            "url": null
                          },
                          {
                            "kind": "news",
                            "id": 1179,
                            "title": "Wacker Chemie may close Tennessee polysilicon plant amid U.S. trade measures",
                            "root_cause": "geopolitical_action",
                            "country": "United States",
                            "severity": "high",
                            "published_date": "2026-09-04T00:00:00.000Z",
                            "url": "https://www.reuters.com/business/trumps-bid-shield-chip-supply-chain-could-backfire-tennessee-2026-09-04/"
                          },
                          {
                            "kind": "news",
                            "id": 1189,
                            "title": "China imposes provisional duties on Japanese semiconductor chemical",
                            "root_cause": "regulatory_change",
                            "country": "China",
                            "severity": "medium",
                            "published_date": "2026-09-09T00:00:00.000Z",
                            "url": "https://hk.epochtimes.com/news/2026-09-09/80403878"
                          },
                          {
                            "kind": "news",
                            "id": 1166,
                            "title": "China imposes helium export controls affecting global high‑tech supply",
                            "root_cause": "regulatory_change",
                            "country": "China",
                            "severity": "high",
                            "published_date": "2026-07-11T00:00:00.000Z",
                            "url": "https://www.caixinglobal.com/2026-07-11/china-bans-helium-exports-as-global-supply-crunch-hits-chipmaking-gas-102462943.html"
                          }
                        ]
                      },
                      "supply": {
                        "stock": {
                          "value": 24453,
                          "unit": "units"
                        },
                        "lead_time_median": {
                          "value": 369,
                          "unit": "days"
                        },
                        "lead_time_min": {
                          "value": 220,
                          "unit": "days"
                        },
                        "lead_time_max": {
                          "value": 373,
                          "unit": "days"
                        },
                        "distributor_count": 4,
                        "scope": "manufacturer",
                        "distributors": [
                          {
                            "name": "Farnell",
                            "country": "United Kingdom",
                            "country_code": "GB",
                            "regions": [
                              "Europe"
                            ],
                            "subregion": "Northern Europe",
                            "stock": {
                              "value": 21050,
                              "unit": "units"
                            }
                          },
                          {
                            "name": "element14",
                            "country": "Singapore",
                            "country_code": "SG",
                            "regions": [
                              "Asia"
                            ],
                            "subregion": "South-eastern Asia",
                            "stock": {
                              "value": 3174,
                              "unit": "units"
                            }
                          }
                        ]
                      }
                    },
                    {
                      "line": 3,
                      "mpn": "6305-2RS",
                      "manufacturer": "SKF",
                      "quantity": 10,
                      "status": "resolved",
                      "notes": [],
                      "part": {
                        "identified_mpn": null,
                        "category": "Bearings & Linear Guides",
                        "lifecycle": null,
                        "manufacturing_country": "China",
                        "manufacturing_country_code": "CN"
                      },
                      "risk": {
                        "composite": null,
                        "tier": null,
                        "reason": "No distributor has stock or lead-time figures for this line, so no overall score is computed.",
                        "drivers": {
                          "geography": {
                            "tier": "medium",
                            "interpretation": "Medium. Reflects the risk profile of China."
                          },
                          "disruption_exposure": {
                            "tier": "low",
                            "interpretation": "Low. Little exposure to active disruptions. 1 event or shipping condition is listed in `events`."
                          },
                          "lead_time_volatility": null,
                          "gaps": {
                            "tier": "low",
                            "interpretation": "Low. Sourcing gaps on this line: no supplier named."
                          }
                        },
                        "gap_flags": [
                          "no_supplier"
                        ],
                        "events": [
                          {
                            "kind": "sea_route",
                            "id": null,
                            "title": "Red Sea / Suez: ship transits 54% below the usual level for this time of year",
                            "root_cause": null,
                            "country": null,
                            "severity": null,
                            "published_date": "2026-09-20T00:00:00.000Z",
                            "url": null
                          }
                        ]
                      },
                      "supply": {
                        "stock": {
                          "value": null,
                          "unit": "units",
                          "reason": "No distributor data available for this line."
                        },
                        "lead_time_median": {
                          "value": null,
                          "unit": "days",
                          "reason": "No distributor quotes a lead time for this line."
                        },
                        "lead_time_min": {
                          "value": null,
                          "unit": "days",
                          "reason": "No distributor quotes a lead time."
                        },
                        "lead_time_max": {
                          "value": null,
                          "unit": "days",
                          "reason": "No distributor quotes a lead time."
                        },
                        "distributor_count": 0,
                        "scope": null,
                        "distributors": []
                      }
                    }
                  ]
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "400": {
            "description": "`country` is not a country code.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, revoked or expired key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The caller signed in through an AI app without the permission this call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the error is `insufficient_scope`. Or the AI app was disconnected in the console; the error is `app_disconnected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "404": {
            "description": "No BOM with that id belongs to your organization (with a key made with `POST /v1/keys`, to that customer).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "429": {
            "description": "Your organization has used its calls for this minute or this day. Wait the number of seconds in `Retry-After`, then retry.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the limit resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteBom",
        "tags": [
          "BOMs"
        ],
        "summary": "Delete a BOM",
        "description": "Removes the BOM and every line. Irreversible.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The BOM id returned by `POST /v1/bom`."
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BomDeleted"
                },
                "example": {
                  "as_of": "2026-09-26T06:48:32.667Z",
                  "summary": "BOM 7df7d0aa-ba87-4a66-bd6d-01c033bafe4e and all of its lines were deleted.",
                  "notice": "Informational only. Risk scores, stock levels, lead times and alternate-part suggestions may be incomplete, delayed or inaccurate. Independently verify before making procurement, engineering, compliance, safety or sourcing decisions.",
                  "deleted": {
                    "id": "7df7d0aa-ba87-4a66-bd6d-01c033bafe4e"
                  }
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, revoked or expired key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The caller signed in through an AI app without the permission this call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the error is `insufficient_scope`. Or the AI app was disconnected in the console; the error is `app_disconnected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "404": {
            "description": "No BOM with that id belongs to your organization (with a key made with `POST /v1/keys`, to that customer).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "429": {
            "description": "Your organization has used its calls for this minute or this day. Wait the number of seconds in `Retry-After`, then retry.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the limit resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/events": {
      "get": {
        "operationId": "listEvents",
        "tags": [
          "Events"
        ],
        "summary": "List disruption events",
        "description": "Active supply-chain disruption events, newest first, one page at a time: what happened, where, how serious, which component categories it affects, what it means for a buyer, and the news reports of it. Filter by country, region, category or how recently it was first recorded. When `next_cursor` is not null, call again with `cursor` set to it for the next page.",
        "parameters": [
          {
            "name": "country",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "example": "CN",
            "description": "ISO 3166-1 alpha-2 code. Only events in that country."
          },
          {
            "name": "region",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "Asia-Pacific",
                "Europe",
                "North America",
                "Middle East",
                "Latin America",
                "Africa",
                "Global"
              ]
            },
            "description": "Only events in that region. Regions as supply-chain news is grouped, not the UN regions on distributors; to match a distributor or a part's origin, filter by `country` (an ISO 3166-1 alpha-2 code, as `country_code` on distributors and `manufacturing_country_code` on parts and BOM lines)."
          },
          {
            "name": "category",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "semiconductors",
                "electronics",
                "pcb",
                "connectors",
                "sensors",
                "motors",
                "drives",
                "power_supplies",
                "batteries",
                "cables_wire",
                "rf_microwave",
                "optics",
                "bearings",
                "mechanical",
                "hydraulics",
                "pneumatics",
                "seals_gaskets",
                "fluid_handling",
                "thermal",
                "industrial_automation",
                "filtration",
                "safety_equipment",
                "metals",
                "chemicals",
                "petrochemicals",
                "plastics",
                "raw_materials",
                "pharmaceuticals",
                "active_ingredients",
                "medical_devices",
                "agriculture",
                "fertilizers",
                "commodities",
                "automotive",
                "energy",
                "logistics"
              ]
            },
            "example": "semiconductors",
            "description": "Only events affecting that component category."
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 90
            },
            "description": "Only events first recorded in the last N days, 1 to 90. Omit it for every active event."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            },
            "description": "Events per page, 1 to 100. Defaults to 50."
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "`next_cursor` from the previous page, unchanged. Omit it for the first page."
          }
        ],
        "responses": {
          "200": {
            "description": "The events. The list can be empty.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventList"
                },
                "example": {
                  "as_of": "2026-10-01T12:16:41.913Z",
                  "summary": "2 active events, newest first. More are available: call again with `cursor` set to `next_cursor`.",
                  "notice": "Informational only. Risk scores, stock levels, lead times and alternate-part suggestions may be incomplete, delayed or inaccurate. Independently verify before making procurement, engineering, compliance, safety or sourcing decisions.",
                  "events": [
                    {
                      "id": 1205,
                      "title": "Ukraine Black Sea port attacks disrupt grain logistics in Odesa",
                      "summary": "Military attacks on ports and logistics facilities in Ukraine's Odesa and Mykolaiv regions have crippled grain loading operations, bringing Black Sea grain shipments close to a standstill. The disruption creates immediate logistics bottlenecks and threatens shortages of Ukrainian grain commodities on global markets. Buyers of Ukrainian commodities should track port availability and explore alternative supply sources.",
                      "severity": "high",
                      "report_status": "confirmed",
                      "country": "Ukraine",
                      "country_code": "UA",
                      "region": "Europe",
                      "categories": [
                        "commodities",
                        "logistics"
                      ],
                      "root_cause": "geopolitical_action",
                      "supply_chain_impact": [
                        "logistics_disruption",
                        "shortage"
                      ],
                      "sourcing_implication": "Attacks on Odesa and Mykolaiv ports hinder Ukrainian grain shipments, prompting buyers of Ukrainian commodities to monitor port availability and consider alternative sources.",
                      "recommended_action": "monitor",
                      "sourcing_urgency": "critical",
                      "regions_exposed": [
                        "Europe",
                        "North America",
                        "Asia-Pacific",
                        "Latin America",
                        "Africa"
                      ],
                      "lead_time": {
                        "text": "as much as 12 days",
                        "source": {
                          "sentence": "Özdemir said waiting times at the Sulina entrance to the Danube had increased from two or three days to as much as 12 days, adding demurrage costs and making delivery schedules harder to manage.",
                          "url": "https://millermagazine.com/en/blog/black-sea-disruption-shifts-grain-trade-focus-from-price-to-reliable-delivery-7046"
                        },
                        "interpretation": "Reported by millermagazine.com on September 30, 2026 for shipments affected by this event."
                      },
                      "shipping_check": null,
                      "source_urls": [
                        "http://rmb.reuters.com/rmd/rss/item/tag:reuters.com,2026:newsml_KBN3VD06W?channel=BEQ259",
                        "https://www.livemint.com/market/wheat-falls-as-markets-monitor-black-sea-export-prospects/amp-11790558644308.html"
                      ],
                      "published_date": "2026-09-28T00:00:00.000Z",
                      "first_seen": "2026-09-28T04:46:31.972Z"
                    },
                    {
                      "id": 1199,
                      "title": "Russian shelling destroys Kärcher warehouse in Ukraine",
                      "summary": "A warehouse storing Kärcher cleaning equipment in Ukraine was destroyed by Russian shelling, halting local distribution and delaying orders. The loss creates a short‑term shortage of cleaning products for regional customers and may increase logistics costs as alternative routes are arranged.",
                      "severity": "medium",
                      "report_status": "rumor",
                      "country": "Ukraine",
                      "country_code": "UA",
                      "region": "Europe",
                      "categories": [
                        "mechanical"
                      ],
                      "root_cause": "geopolitical_action",
                      "supply_chain_impact": [
                        "logistics_disruption",
                        "shortage"
                      ],
                      "sourcing_implication": "The destruction of the Kärcher warehouse in Ukraine disrupts distribution of cleaning equipment, requiring customers to source from other regional depots.",
                      "recommended_action": "monitor",
                      "sourcing_urgency": "medium",
                      "regions_exposed": [
                        "Europe"
                      ],
                      "lead_time": {
                        "text": "+3-7 days",
                        "source": null,
                        "interpretation": "An estimate from the event's description."
                      },
                      "shipping_check": null,
                      "source_urls": [
                        "https://antikor.info/en/articles/865669-rossijskij_obstrel_unichtohil_sklad_k228rcher_v_ukraine_tehnika_i_produktsija_polnostjju_sgoreli"
                      ],
                      "published_date": "2026-09-27T00:00:00.000Z",
                      "first_seen": "2026-09-28T04:45:13.583Z"
                    }
                  ],
                  "next_cursor": "MTE5OQ"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "400": {
            "description": "A parameter is outside its allowed values, or `cursor` is not one we issued.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, revoked or expired key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "This request was not run and nothing was charged. The error code names the case. `credits_exhausted`: your plan's credits for this month are used up; upgrade the plan in the PartFinder console. `payment_method_required`: your organization is on pay as you go with no payment method; add one in the console, or choose a plan. `payment_failed`: pay as you go is paused because the last payment failed; update the payment method in the console. `usage_limit_reached`: your organization, on pay as you go, has reached its limit for unpaid usage; calls resume once the current payment goes through. With a key made with `POST /v1/keys`, the `summary` tells your customer to contact your organization, and the answer carries no `billing_url`. The `summary` can be relayed as is; retry once the case is resolved.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "credits_exhausted",
                  "summary": "Your organization's credits for this month are used up, so this request was not run. Upgrade your plan in the PartFinder console at https://console.partfinder.dev/settings/billing, then try again.",
                  "reason": "This request needs 1 credit and 0 are left.",
                  "billing_url": "https://console.partfinder.dev/settings/billing"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "403": {
            "description": "The caller signed in through an AI app without the permission this call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the error is `insufficient_scope`. Or the AI app was disconnected in the console; the error is `app_disconnected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "429": {
            "description": "Your organization has used its calls for this minute or this day. Wait the number of seconds in `Retry-After`, then retry.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the limit resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/events/{id}": {
      "get": {
        "operationId": "getEvent",
        "tags": [
          "Events"
        ],
        "summary": "Get a disruption event",
        "description": "One event in full. The `id` is the one `GET /v1/events`, `/v1/parts` and `/v1/bom` return.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "description": "The event id."
          }
        ],
        "responses": {
          "200": {
            "description": "The event.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventResponse"
                },
                "example": {
                  "as_of": "2026-10-01T12:16:43.703Z",
                  "summary": "Ukraine Black Sea port attacks disrupt grain logistics in Odesa (high severity, Ukraine).",
                  "notice": "Informational only. Risk scores, stock levels, lead times and alternate-part suggestions may be incomplete, delayed or inaccurate. Independently verify before making procurement, engineering, compliance, safety or sourcing decisions.",
                  "event": {
                    "id": 1205,
                    "title": "Ukraine Black Sea port attacks disrupt grain logistics in Odesa",
                    "summary": "Military attacks on ports and logistics facilities in Ukraine's Odesa and Mykolaiv regions have crippled grain loading operations, bringing Black Sea grain shipments close to a standstill. The disruption creates immediate logistics bottlenecks and threatens shortages of Ukrainian grain commodities on global markets. Buyers of Ukrainian commodities should track port availability and explore alternative supply sources.",
                    "severity": "high",
                    "report_status": "confirmed",
                    "country": "Ukraine",
                    "country_code": "UA",
                    "region": "Europe",
                    "categories": [
                      "commodities",
                      "logistics"
                    ],
                    "root_cause": "geopolitical_action",
                    "supply_chain_impact": [
                      "logistics_disruption",
                      "shortage"
                    ],
                    "sourcing_implication": "Attacks on Odesa and Mykolaiv ports hinder Ukrainian grain shipments, prompting buyers of Ukrainian commodities to monitor port availability and consider alternative sources.",
                    "recommended_action": "monitor",
                    "sourcing_urgency": "critical",
                    "regions_exposed": [
                      "Europe",
                      "North America",
                      "Asia-Pacific",
                      "Latin America",
                      "Africa"
                    ],
                    "lead_time": {
                      "text": "as much as 12 days",
                      "source": {
                        "sentence": "Özdemir said waiting times at the Sulina entrance to the Danube had increased from two or three days to as much as 12 days, adding demurrage costs and making delivery schedules harder to manage.",
                        "url": "https://millermagazine.com/en/blog/black-sea-disruption-shifts-grain-trade-focus-from-price-to-reliable-delivery-7046"
                      },
                      "interpretation": "Reported by millermagazine.com on September 30, 2026 for shipments affected by this event."
                    },
                    "shipping_check": null,
                    "source_urls": [
                      "http://rmb.reuters.com/rmd/rss/item/tag:reuters.com,2026:newsml_KBN3VD06W?channel=BEQ259",
                      "https://www.livemint.com/market/wheat-falls-as-markets-monitor-black-sea-export-prospects/amp-11790558644308.html"
                    ],
                    "published_date": "2026-09-28T00:00:00.000Z",
                    "first_seen": "2026-09-28T04:46:31.972Z"
                  }
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, revoked or expired key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "This request was not run and nothing was charged. The error code names the case. `credits_exhausted`: your plan's credits for this month are used up; upgrade the plan in the PartFinder console. `payment_method_required`: your organization is on pay as you go with no payment method; add one in the console, or choose a plan. `payment_failed`: pay as you go is paused because the last payment failed; update the payment method in the console. `usage_limit_reached`: your organization, on pay as you go, has reached its limit for unpaid usage; calls resume once the current payment goes through. With a key made with `POST /v1/keys`, the `summary` tells your customer to contact your organization, and the answer carries no `billing_url`. The `summary` can be relayed as is; retry once the case is resolved.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "credits_exhausted",
                  "summary": "Your organization's credits for this month are used up, so this request was not run. Upgrade your plan in the PartFinder console at https://console.partfinder.dev/settings/billing, then try again.",
                  "reason": "This request needs 1 credit and 0 are left.",
                  "billing_url": "https://console.partfinder.dev/settings/billing"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "403": {
            "description": "The caller signed in through an AI app without the permission this call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the error is `insufficient_scope`. Or the AI app was disconnected in the console; the error is `app_disconnected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "404": {
            "description": "No active event has that id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "429": {
            "description": "Your organization has used its calls for this minute or this day. Wait the number of seconds in `Retry-After`, then retry.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the limit resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/events/countries": {
      "get": {
        "operationId": "listEventCountries",
        "tags": [
          "Events"
        ],
        "summary": "Disruptions by country",
        "description": "The countries on the risk map on partfinder.dev right now: recent events still unfolding, how many, and the worst severity among them. It is not every country with an active event: a country absent here can still have active events, listed by `GET /v1/events?country=`. Open the most severe of a country's listed events with `GET /v1/events/{id}` and `top_event_id`.",
        "responses": {
          "200": {
            "description": "The countries, most severe first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventCountries"
                },
                "example": {
                  "as_of": "2026-10-01T12:39:27.108Z",
                  "summary": "10 countries are on the risk map right now (recent events still unfolding); 8 at high or critical. A country not listed can still have active events: list them with GET /v1/events?country=.",
                  "notice": "Informational only. Risk scores, stock levels, lead times and alternate-part suggestions may be incomplete, delayed or inaccurate. Independently verify before making procurement, engineering, compliance, safety or sourcing decisions.",
                  "countries": [
                    {
                      "country": "United States",
                      "country_code": "US",
                      "event_count": 5,
                      "worst_severity": "high",
                      "top_event_id": 1194
                    },
                    {
                      "country": "Ukraine",
                      "country_code": "UA",
                      "event_count": 2,
                      "worst_severity": "high",
                      "top_event_id": 1205
                    },
                    {
                      "country": "Iran",
                      "country_code": "IR",
                      "event_count": 2,
                      "worst_severity": "high",
                      "top_event_id": 1202
                    },
                    {
                      "country": "Germany",
                      "country_code": "DE",
                      "event_count": 1,
                      "worst_severity": "high",
                      "top_event_id": 1211
                    },
                    {
                      "country": "Bangladesh",
                      "country_code": "BD",
                      "event_count": 1,
                      "worst_severity": "high",
                      "top_event_id": 1210
                    },
                    {
                      "country": "Mexico",
                      "country_code": "MX",
                      "event_count": 1,
                      "worst_severity": "high",
                      "top_event_id": 1200
                    },
                    {
                      "country": "Chile",
                      "country_code": "CL",
                      "event_count": 1,
                      "worst_severity": "high",
                      "top_event_id": 1198
                    },
                    {
                      "country": "Netherlands",
                      "country_code": "NL",
                      "event_count": 1,
                      "worst_severity": "high",
                      "top_event_id": 1195
                    },
                    {
                      "country": "Saudi Arabia",
                      "country_code": "SA",
                      "event_count": 1,
                      "worst_severity": "medium",
                      "top_event_id": 1204
                    },
                    {
                      "country": "Philippines",
                      "country_code": "PH",
                      "event_count": 1,
                      "worst_severity": "medium",
                      "top_event_id": 1197
                    }
                  ]
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, revoked or expired key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "This request was not run and nothing was charged. The error code names the case. `credits_exhausted`: your plan's credits for this month are used up; upgrade the plan in the PartFinder console. `payment_method_required`: your organization is on pay as you go with no payment method; add one in the console, or choose a plan. `payment_failed`: pay as you go is paused because the last payment failed; update the payment method in the console. `usage_limit_reached`: your organization, on pay as you go, has reached its limit for unpaid usage; calls resume once the current payment goes through. With a key made with `POST /v1/keys`, the `summary` tells your customer to contact your organization, and the answer carries no `billing_url`. The `summary` can be relayed as is; retry once the case is resolved.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "credits_exhausted",
                  "summary": "Your organization's credits for this month are used up, so this request was not run. Upgrade your plan in the PartFinder console at https://console.partfinder.dev/settings/billing, then try again.",
                  "reason": "This request needs 1 credit and 0 are left.",
                  "billing_url": "https://console.partfinder.dev/settings/billing"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "403": {
            "description": "The caller signed in through an AI app without the permission this call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the error is `insufficient_scope`. Or the AI app was disconnected in the console; the error is `app_disconnected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "429": {
            "description": "Your organization has used its calls for this minute or this day. Wait the number of seconds in `Retry-After`, then retry.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the limit resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/events/shipping": {
      "get": {
        "operationId": "listShippingConditions",
        "tags": [
          "Events"
        ],
        "summary": "Port and sea-route activity",
        "description": "Port calls per country and ship transits per sea route over the latest week, as a change in percent against their usual level for this time of year. A drop at a port or a strait delays shipments through it.",
        "responses": {
          "200": {
            "description": "The readings, furthest below usual first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ShippingConditions"
                },
                "example": {
                  "as_of": "2026-10-01T12:39:35.891Z",
                  "summary": "Port calls by country and ship transits by sea route over the latest week, against their usual level for this time of year, furthest below usual first.",
                  "notice": "Informational only. Risk scores, stock levels, lead times and alternate-part suggestions may be incomplete, delayed or inaccurate. Independently verify before making procurement, engineering, compliance, safety or sourcing decisions.",
                  "countries": [
                    {
                      "country": "Ukraine",
                      "country_code": "UA",
                      "change_from_normal_percent": -97,
                      "through": "2026-09-18",
                      "interpretation": "Port calls in Ukraine are 97% below the usual level for this time of year."
                    },
                    {
                      "country": "Kuwait",
                      "country_code": "KW",
                      "change_from_normal_percent": -95,
                      "through": "2026-09-18",
                      "interpretation": "Port calls in Kuwait are 95% below the usual level for this time of year."
                    },
                    {
                      "country": "Saudi Arabia",
                      "country_code": "SA",
                      "change_from_normal_percent": -74,
                      "through": "2026-09-18",
                      "interpretation": "Port calls in Saudi Arabia are 74% below the usual level for this time of year."
                    }
                  ],
                  "sea_routes": [
                    {
                      "name": "Red Sea / Suez",
                      "change_from_normal_percent": -54,
                      "through": "2026-09-20",
                      "interpretation": "Ship transits through Red Sea / Suez are 54% below the usual level for this time of year."
                    }
                  ]
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, revoked or expired key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "This request was not run and nothing was charged. The error code names the case. `credits_exhausted`: your plan's credits for this month are used up; upgrade the plan in the PartFinder console. `payment_method_required`: your organization is on pay as you go with no payment method; add one in the console, or choose a plan. `payment_failed`: pay as you go is paused because the last payment failed; update the payment method in the console. `usage_limit_reached`: your organization, on pay as you go, has reached its limit for unpaid usage; calls resume once the current payment goes through. With a key made with `POST /v1/keys`, the `summary` tells your customer to contact your organization, and the answer carries no `billing_url`. The `summary` can be relayed as is; retry once the case is resolved.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "credits_exhausted",
                  "summary": "Your organization's credits for this month are used up, so this request was not run. Upgrade your plan in the PartFinder console at https://console.partfinder.dev/settings/billing, then try again.",
                  "reason": "This request needs 1 credit and 0 are left.",
                  "billing_url": "https://console.partfinder.dev/settings/billing"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "403": {
            "description": "The caller signed in through an AI app without the permission this call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the error is `insufficient_scope`. Or the AI app was disconnected in the console; the error is `app_disconnected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "429": {
            "description": "Your organization has used its calls for this minute or this day. Wait the number of seconds in `Retry-After`, then retry.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the limit resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/search": {
      "post": {
        "operationId": "searchParts",
        "tags": [
          "Search"
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "example": "7b1e0c94-2f6a-4d38-9a51-c0e8d4f2a716",
            "description": "Set by your client code, not by a person or a model: generate a new value (a UUID) for each search, and send the same value again only when retrying that same search after a timeout. A retry with the same key returns the search the first call started and starts no second one. A key is recognised only from the same API key that sent it. Optional."
          }
        ],
        "summary": "Search for parts",
        "description": "Find parts for a part number or a description: the part itself and comparable parts from the same or other makers, each with its product page, datasheet, the specifications that matter for this kind of part, and its stock and lead time at distributors. Answers `202` at once; read `GET /v1/search/{id}` after 35 seconds, then every 15 seconds, until `status` is no longer `processing`: `complete` with the rows, or `failed`. Each answer while it runs states the wait in `summary` and `Retry-After`. A query that is not about a part is refused with `422`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SearchRequest"
              },
              "example": {
                "query": "TPS54331DR"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted. The search has started; a retry with an `Idempotency-Key` already used returns the search that key started, with its current `status`.",
            "headers": {
              "Location": {
                "description": "The absolute URL of the search, `GET /v1/search/{id}` on the server this request reached.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait before reading the search: 35. A retried request with an `Idempotency-Key` already used sends 15 while that search is still running, and no header once it has ended: read it at once.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchAccepted"
                },
                "example": {
                  "as_of": "2026-10-10T13:19:57.540Z",
                  "summary": "Searching for \"TPS54331DR\". Poll GET /v1/search/e8413914-cea8-46b0-9c6b-8207a4157fc0 after 35 seconds, then every 15 seconds, until `status` is no longer `processing`.",
                  "notice": "Informational only. Risk scores, stock levels, lead times and alternate-part suggestions may be incomplete, delayed or inaccurate. Independently verify before making procurement, engineering, compliance, safety or sourcing decisions.",
                  "id": "e8413914-cea8-46b0-9c6b-8207a4157fc0",
                  "status": "processing"
                }
              }
            }
          },
          "400": {
            "description": "The body is not valid: `query` is missing or empty.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, revoked or expired key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "This request was not run and nothing was charged. The error code names the case. `credits_exhausted`: your plan's credits for this month are used up; upgrade the plan in the PartFinder console. `payment_method_required`: your organization is on pay as you go with no payment method; add one in the console, or choose a plan. `payment_failed`: pay as you go is paused because the last payment failed; update the payment method in the console. `usage_limit_reached`: your organization, on pay as you go, has reached its limit for unpaid usage; calls resume once the current payment goes through. With a key made with `POST /v1/keys`, the `summary` tells your customer to contact your organization, and the answer carries no `billing_url`. The `summary` can be relayed as is; retry once the case is resolved.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "credits_exhausted",
                  "summary": "Your organization's credits for this month are used up, so this request was not run. Upgrade your plan in the PartFinder console at https://console.partfinder.dev/settings/billing, then try again.",
                  "reason": "This request needs 40 credits and 0 are left.",
                  "billing_url": "https://console.partfinder.dev/settings/billing"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "403": {
            "description": "The caller signed in through an AI app without the permission this call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the error is `insufficient_scope`. Or the AI app was disconnected in the console; the error is `app_disconnected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "422": {
            "description": "The query is not a search for a part. Nothing was started.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "429": {
            "description": "Your organization has used its calls for this minute or this day. Wait the number of seconds in `Retry-After`, then retry.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the limit resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The search could not be started. Nothing was stored; retry shortly.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listSearches",
        "tags": [
          "Search"
        ],
        "summary": "List your searches",
        "description": "Every search your organization has run (with a key made with `POST /v1/keys`, only that customer's), newest first, one page at a time. When `next_cursor` is not null, call again with `cursor` set to it for the next page. Read one with `GET /v1/search/{id}` for its rows.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            },
            "description": "Searches per page, 1 to 100. Defaults to 50."
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "`next_cursor` from the previous page, unchanged. Omit it for the first page."
          }
        ],
        "responses": {
          "200": {
            "description": "The list.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchList"
                },
                "example": {
                  "as_of": "2026-10-10T13:21:05.000Z",
                  "summary": "1 search, newest first.",
                  "notice": "Informational only. Risk scores, stock levels, lead times and alternate-part suggestions may be incomplete, delayed or inaccurate. Independently verify before making procurement, engineering, compliance, safety or sourcing decisions.",
                  "searches": [
                    {
                      "id": "e8413914-cea8-46b0-9c6b-8207a4157fc0",
                      "status": "complete",
                      "query": "TPS54331DR",
                      "created_at": "2026-10-10T13:19:57.082Z",
                      "completed_at": "2026-10-10T13:20:39.606Z",
                      "rows": 2
                    }
                  ],
                  "next_cursor": null
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "400": {
            "description": "`limit` is outside 1-100, or `cursor` is not one issued to you.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, revoked or expired key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The caller signed in through an AI app without the permission this call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the error is `insufficient_scope`. Or the AI app was disconnected in the console; the error is `app_disconnected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "429": {
            "description": "Your organization has used its calls for this minute or this day. Wait the number of seconds in `Retry-After`, then retry.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the limit resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/search/{id}": {
      "get": {
        "operationId": "getSearch",
        "tags": [
          "Search"
        ],
        "summary": "Get a search",
        "description": "A search and, once `status` is `complete`, the parts it found. While `status` is `processing`, read it again after 15 seconds, the wait `summary` and `Retry-After` state; each read counts as a call. `failed` means the search could not be completed: submit it again.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The search id."
          }
        ],
        "responses": {
          "200": {
            "description": "The search.",
            "headers": {
              "Retry-After": {
                "description": "Sent only while `status` is `processing`: seconds to wait before reading the search again, 15.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Search"
                },
                "example": {
                  "as_of": "2026-10-10T13:20:39.606Z",
                  "summary": "2 parts found for \"TPS54331DR\": TPS54331DR (Texas Instruments), TPS5430DDAR (Texas Instruments). 2 of them are in stock at a distributor.",
                  "notice": "Informational only. Risk scores, stock levels, lead times and alternate-part suggestions may be incomplete, delayed or inaccurate. Independently verify before making procurement, engineering, compliance, safety or sourcing decisions.",
                  "id": "e8413914-cea8-46b0-9c6b-8207a4157fc0",
                  "status": "complete",
                  "query": "TPS54331DR",
                  "created_at": "2026-10-10T13:19:57.082Z",
                  "completed_at": "2026-10-10T13:20:39.606Z",
                  "columns": [
                    "Input Voltage Range",
                    "Output Voltage",
                    "Output Current",
                    "Efficiency"
                  ],
                  "rows": [
                    {
                      "name": "TPS54331DR",
                      "manufacturer": "Texas Instruments",
                      "product_url": "https://www.ti.com/product/TPS54331/part-details/TPS54331DR",
                      "datasheet_url": "https://www.ti.com/lit/gpn/tps54331",
                      "lifecycle": "Active",
                      "specs": {
                        "Input Voltage Range": "3.5 V to 28 V",
                        "Output Voltage": "adjustable down to 0.8 V",
                        "Output Current": "3 A continuous",
                        "Efficiency": null
                      },
                      "supply": {
                        "stock": {
                          "value": 1148628,
                          "unit": "units"
                        },
                        "distributor_count": 22,
                        "lead_time_median": {
                          "value": 84,
                          "unit": "days"
                        }
                      }
                    },
                    {
                      "name": "TPS5430DDAR",
                      "manufacturer": "Texas Instruments",
                      "product_url": "https://www.ti.com/product/TPS5430/part-details/TPS5430DDAR",
                      "datasheet_url": "https://www.ti.com/lit/ds/symlink/tps5430.pdf",
                      "lifecycle": "Active",
                      "specs": {
                        "Input Voltage Range": "5.5 V to 36 V",
                        "Output Voltage": "adjustable down to 1.22 V",
                        "Output Current": "3 A continuous",
                        "Efficiency": "up to 95 %"
                      },
                      "supply": {
                        "stock": {
                          "value": 1192476,
                          "unit": "units"
                        },
                        "distributor_count": 22,
                        "lead_time_median": {
                          "value": 140,
                          "unit": "days"
                        }
                      }
                    }
                  ],
                  "insights": {
                    "key_specs": [
                      "Input Voltage Range",
                      "Output Voltage",
                      "Output Current",
                      "Efficiency"
                    ],
                    "applications": [
                      "Portable electronics requiring 3 A continuous output current",
                      "Systems operating with input voltages ranging from 3.5 V to 36 V"
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, revoked or expired key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The caller signed in through an AI app without the permission this call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the error is `insufficient_scope`. Or the AI app was disconnected in the console; the error is `app_disconnected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "404": {
            "description": "No search with that id belongs to your organization (with a key made with `POST /v1/keys`, to that customer).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "429": {
            "description": "Your organization has used its calls for this minute or this day. Wait the number of seconds in `Retry-After`, then retry.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the limit resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteSearch",
        "tags": [
          "Search"
        ],
        "summary": "Delete a search",
        "description": "Removes the search and its results. Irreversible.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The search id."
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchDeleted"
                },
                "example": {
                  "as_of": "2026-10-10T13:22:10.000Z",
                  "summary": "Search e8413914-cea8-46b0-9c6b-8207a4157fc0 was deleted.",
                  "notice": "Informational only. Risk scores, stock levels, lead times and alternate-part suggestions may be incomplete, delayed or inaccurate. Independently verify before making procurement, engineering, compliance, safety or sourcing decisions.",
                  "deleted": {
                    "id": "e8413914-cea8-46b0-9c6b-8207a4157fc0"
                  }
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, revoked or expired key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The caller signed in through an AI app without the permission this call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the error is `insufficient_scope`. Or the AI app was disconnected in the console; the error is `app_disconnected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "404": {
            "description": "No search with that id belongs to your organization (with a key made with `POST /v1/keys`, to that customer).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "429": {
            "description": "Your organization has used its calls for this minute or this day. Wait the number of seconds in `Retry-After`, then retry.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the limit resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/compare": {
      "post": {
        "operationId": "compareParts",
        "tags": [
          "Compare"
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "example": "3c9d2f71-8b4e-4a06-b1d5-6e2a9f0c4b38",
            "description": "Set by your client code, not by a person or a model: generate a new value (a UUID) for each comparison, and send the same value again only when retrying that same comparison after a timeout. A retry with the same key returns the comparison the first call started and starts no second one. A key is recognised only from the same API key that sent it. Optional."
          }
        ],
        "summary": "Compare parts",
        "description": "Put parts side by side on the specifications that matter for this kind of part, read from each part's datasheet. To compare parts you name with each other, send them all in `parts`, the first being the reference. Send `mpn` alone only to compare one part with its alternates. Every other part gets one verdict per specification against the reference (`same`, `covers`, `narrower`, `higher`, `lower`, `different`, `not_stated`) and a count of the specifications that are the same or covered, plus its stock and lead time at distributors. Answers `202` at once; read `GET /v1/compare/{id}` after 10 seconds, then every 9 seconds, until `status` is no longer `processing`: `complete` with the parts, or `failed`. Each answer while it runs states the wait in `summary` and `Retry-After`. A part that is not one specific part is refused with `422`, a part no distributor lists with `404`; the refusal names the part and nothing is started.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CompareRequest"
              },
              "example": {
                "parts": [
                  {
                    "mpn": "TPS54331DR"
                  },
                  {
                    "mpn": "TPS5430DDAR"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted. The comparison has started; a retry with an `Idempotency-Key` already used returns the comparison that key started, with its current `status`.",
            "headers": {
              "Location": {
                "description": "The absolute URL of the comparison, `GET /v1/compare/{id}` on the server this request reached.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait before reading the comparison: 10. A retried request with an `Idempotency-Key` already used sends 9 while that comparison is still running, and no header once it has ended: read it at once.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CompareAccepted"
                },
                "example": {
                  "as_of": "2026-10-12T02:48:51.650Z",
                  "summary": "Comparing TPS54331DR. Poll GET /v1/compare/f2d22c25-e694-428b-bbb8-3948e60d3c7b after 10 seconds, then every 9 seconds, until `status` is no longer `processing`.",
                  "notice": "Informational only. Risk scores, stock levels, lead times and alternate-part suggestions may be incomplete, delayed or inaccurate. Independently verify before making procurement, engineering, compliance, safety or sourcing decisions.",
                  "id": "f2d22c25-e694-428b-bbb8-3948e60d3c7b",
                  "status": "processing"
                }
              }
            }
          },
          "400": {
            "description": "The body is not valid: neither or both of `mpn` and `parts`, or `parts` with fewer than 2 or more than 6 parts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, revoked or expired key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "This request was not run and nothing was charged. The error code names the case. `credits_exhausted`: your plan's credits for this month are used up; upgrade the plan in the PartFinder console. `payment_method_required`: your organization is on pay as you go with no payment method; add one in the console, or choose a plan. `payment_failed`: pay as you go is paused because the last payment failed; update the payment method in the console. `usage_limit_reached`: your organization, on pay as you go, has reached its limit for unpaid usage; calls resume once the current payment goes through. With a key made with `POST /v1/keys`, the `summary` tells your customer to contact your organization, and the answer carries no `billing_url`. The `summary` can be relayed as is; retry once the case is resolved.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "credits_exhausted",
                  "summary": "Your organization's credits for this month are used up, so this request was not run. Upgrade your plan in the PartFinder console at https://console.partfinder.dev/settings/billing, then try again.",
                  "reason": "This request needs 8 credits and 0 are left.",
                  "billing_url": "https://console.partfinder.dev/settings/billing"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "403": {
            "description": "The caller signed in through an AI app without the permission this call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the error is `insufficient_scope`. Or the AI app was disconnected in the console; the error is `app_disconnected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "404": {
            "description": "No distributor catalogue lists a part number you sent exactly; `summary` names it. Usually a typo or a partial part number. When listed part numbers begin with what you sent, `did_you_mean` carries up to five of them. Nothing was started.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "422": {
            "description": "A part you sent does not identify one specific part: a question, a category, or a generic specification; `summary` names it. When it described a class of parts, `did_you_mean` carries real part numbers to retry with. Nothing was started.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "429": {
            "description": "Your organization has used its calls for this minute or this day. Wait the number of seconds in `Retry-After`, then retry.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the limit resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The comparison could not be started. Nothing was stored; retry shortly.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "503": {
            "description": "We could not confirm a part number you sent is real, because the supplier data needed to check it is momentarily unavailable. Nothing was started. Temporary: retry shortly.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listComparisons",
        "tags": [
          "Compare"
        ],
        "summary": "List your comparisons",
        "description": "Every comparison your organization has run (with a key made with `POST /v1/keys`, only that customer's), newest first, one page at a time. When `next_cursor` is not null, call again with `cursor` set to it for the next page. Read one with `GET /v1/compare/{id}` for its parts.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            },
            "description": "Comparisons per page, 1 to 100. Defaults to 50."
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "`next_cursor` from the previous page, unchanged. Omit it for the first page."
          }
        ],
        "responses": {
          "200": {
            "description": "The list.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CompareList"
                },
                "example": {
                  "as_of": "2026-10-12T02:50:02.000Z",
                  "summary": "1 comparison, newest first.",
                  "notice": "Informational only. Risk scores, stock levels, lead times and alternate-part suggestions may be incomplete, delayed or inaccurate. Independently verify before making procurement, engineering, compliance, safety or sourcing decisions.",
                  "comparisons": [
                    {
                      "id": "f2d22c25-e694-428b-bbb8-3948e60d3c7b",
                      "status": "complete",
                      "reference_mpn": "TPS54331DR",
                      "created_at": "2026-10-12T02:48:51.204Z",
                      "completed_at": "2026-10-12T02:49:29.479Z",
                      "parts": 2
                    }
                  ],
                  "next_cursor": null
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "400": {
            "description": "`limit` is outside 1-100, or `cursor` is not one issued to you.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, revoked or expired key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The caller signed in through an AI app without the permission this call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the error is `insufficient_scope`. Or the AI app was disconnected in the console; the error is `app_disconnected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "429": {
            "description": "Your organization has used its calls for this minute or this day. Wait the number of seconds in `Retry-After`, then retry.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the limit resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/compare/{id}": {
      "get": {
        "operationId": "getComparison",
        "tags": [
          "Compare"
        ],
        "summary": "Get a comparison",
        "description": "A comparison and, once `status` is `complete`, its parts: the reference first, then each part compared with it. While `status` is `processing`, read it again after 9 seconds, the wait `summary` and `Retry-After` state; each read counts as a call. `failed` means the comparison could not be completed: submit it again.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The comparison id."
          }
        ],
        "responses": {
          "200": {
            "description": "The comparison.",
            "headers": {
              "Retry-After": {
                "description": "Sent only while `status` is `processing`: seconds to wait before reading the comparison again, 9.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Compare"
                },
                "example": {
                  "as_of": "2026-10-12T02:49:29.479Z",
                  "summary": "TPS54331DR compared with 1 part. TPS5430DDAR: Same or covers on 1 of 4 specs both datasheets state.",
                  "notice": "Informational only. Risk scores, stock levels, lead times and alternate-part suggestions may be incomplete, delayed or inaccurate. Independently verify before making procurement, engineering, compliance, safety or sourcing decisions.",
                  "id": "f2d22c25-e694-428b-bbb8-3948e60d3c7b",
                  "status": "complete",
                  "created_at": "2026-10-12T02:48:51.204Z",
                  "completed_at": "2026-10-12T02:49:29.479Z",
                  "columns": [
                    "Input Voltage Range",
                    "Output Voltage",
                    "Output Current",
                    "Package Type"
                  ],
                  "parts": [
                    {
                      "mpn": "TPS54331DR",
                      "manufacturer": "Texas Instruments",
                      "reference": true,
                      "match": null,
                      "lifecycle": "Active",
                      "specs": {
                        "Input Voltage Range": "3.5 V to 28 V",
                        "Output Voltage": "Adjustable down to 0.8 V",
                        "Output Current": "3 A continuous",
                        "Package Type": "SOIC-8"
                      },
                      "datasheet_url": "https://www.ti.com/lit/gpn/tps54331",
                      "supply": {
                        "stock": {
                          "value": 1148628,
                          "unit": "units"
                        },
                        "distributor_count": 22,
                        "lead_time_median": {
                          "value": 84,
                          "unit": "days"
                        }
                      },
                      "comparison": null
                    },
                    {
                      "mpn": "TPS5430DDAR",
                      "manufacturer": "Texas Instruments",
                      "reference": false,
                      "match": null,
                      "lifecycle": "Active",
                      "specs": {
                        "Input Voltage Range": "5.5 V to 36 V",
                        "Output Voltage": "adjustable down to 1.22 V",
                        "Output Current": "3 A continuous",
                        "Package Type": "SO PowerPAD DDA 8-pin"
                      },
                      "datasheet_url": "https://www.ti.com/lit/ds/symlink/tps5430.pdf",
                      "supply": {
                        "stock": {
                          "value": 1192476,
                          "unit": "units"
                        },
                        "distributor_count": 22,
                        "lead_time_median": {
                          "value": 140,
                          "unit": "days"
                        }
                      },
                      "comparison": {
                        "verdicts": {
                          "Input Voltage Range": "narrower",
                          "Output Voltage": "narrower",
                          "Output Current": "same",
                          "Package Type": "different"
                        },
                        "stated": 4,
                        "same_or_covers": 1,
                        "summary": "Same or covers on 1 of 4 specs both datasheets state."
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, revoked or expired key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The caller signed in through an AI app without the permission this call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the error is `insufficient_scope`. Or the AI app was disconnected in the console; the error is `app_disconnected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "404": {
            "description": "No comparison with that id belongs to your organization (with a key made with `POST /v1/keys`, to that customer).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "429": {
            "description": "Your organization has used its calls for this minute or this day. Wait the number of seconds in `Retry-After`, then retry.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the limit resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteComparison",
        "tags": [
          "Compare"
        ],
        "summary": "Delete a comparison",
        "description": "Removes the comparison and its results. Irreversible.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The comparison id."
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CompareDeleted"
                },
                "example": {
                  "as_of": "2026-10-12T02:51:10.000Z",
                  "summary": "Comparison f2d22c25-e694-428b-bbb8-3948e60d3c7b was deleted.",
                  "notice": "Informational only. Risk scores, stock levels, lead times and alternate-part suggestions may be incomplete, delayed or inaccurate. Independently verify before making procurement, engineering, compliance, safety or sourcing decisions.",
                  "deleted": {
                    "id": "f2d22c25-e694-428b-bbb8-3948e60d3c7b"
                  }
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, revoked or expired key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The caller signed in through an AI app without the permission this call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the error is `insufficient_scope`. Or the AI app was disconnected in the console; the error is `app_disconnected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "404": {
            "description": "No comparison with that id belongs to your organization (with a key made with `POST /v1/keys`, to that customer).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "429": {
            "description": "Your organization has used its calls for this minute or this day. Wait the number of seconds in `Retry-After`, then retry.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the limit resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/rfq": {
      "post": {
        "operationId": "sendRfq",
        "tags": [
          "RFQs"
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "example": "3d9a7f2e-6c41-4b8e-a5d0-7e2f19c4b863",
            "description": "Set by your client code, not by a person or a model: generate a new value (a UUID) for each request for quote, and send the same value again only when retrying that same request after a timeout. A retry with the same key returns the request the first call created and sends no second email. A key is recognised only from the same API key that sent it. Optional."
          }
        ],
        "summary": "Send a request for quote",
        "description": "Email a request for quote to a supplier you name, under the requester's name. This sends a real email to `supplier.email`: every address and name in the request must come from your user, never be guessed or suggested; ask your user for any that are missing. Only these are required: the supplier's email, the requester's name, and the requester's company when it is neither your organization nor, with a key made with `POST /v1/keys`, that customer. Everything else is optional: leave out what they did not give. Send `message` only with text your user wrote; omit it and an email is written for you. The supplier replies to the email as usual, and their replies appear on `GET /v1/rfq/{id}`. A supplier who asked not to receive requests for quote is refused with `409`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RfqRequest"
              },
              "example": {
                "supplier": {
                  "email": "sales@supplier.example",
                  "name": "Example Supplier",
                  "contact_name": "Tom Lee"
                },
                "requester": {
                  "company": "Acme Corp",
                  "contact_name": "Jane Smith",
                  "title": "Procurement Manager"
                },
                "parts": [
                  {
                    "mpn": "TPS54331DR",
                    "manufacturer": "Texas Instruments",
                    "quantity": 500
                  }
                ],
                "needed_by": "2026-11-30"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Sent. The body is the request for quote with its first message.",
            "headers": {
              "Location": {
                "description": "The absolute URL of the request for quote, `GET /v1/rfq/{id}`.",
                "schema": {
                  "type": "string"
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RfqResponse"
                },
                "example": {
                  "as_of": "2026-10-05T16:02:11.482Z",
                  "summary": "Request for quote sent to sales@supplier.example. Replies appear on GET /v1/rfq/5b0f3c1e-8a2d-4f6b-9c7e-1d2a3b4c5d6e.",
                  "notice": "Informational only. Risk scores, stock levels, lead times and alternate-part suggestions may be incomplete, delayed or inaccurate. Independently verify before making procurement, engineering, compliance, safety or sourcing decisions.",
                  "rfq": {
                    "id": "5b0f3c1e-8a2d-4f6b-9c7e-1d2a3b4c5d6e",
                    "status": "sent",
                    "supplier": {
                      "email": "sales@supplier.example",
                      "name": "Example Supplier",
                      "contact_name": "Tom Lee"
                    },
                    "requester": {
                      "company": "Acme Corp",
                      "contact_name": "Jane Smith",
                      "title": "Procurement Manager"
                    },
                    "parts": [
                      {
                        "mpn": "TPS54331DR",
                        "manufacturer": "Texas Instruments",
                        "quantity": 500
                      }
                    ],
                    "needed_by": "2026-11-30",
                    "notify_email": null,
                    "created_at": "2026-10-05T16:02:09.913Z",
                    "first_reply_at": null,
                    "follow_up_count": 0,
                    "replies_received": 0,
                    "quotes_received": 0,
                    "messages": [
                      {
                        "id": "9e4d2c7a-1b3f-4a5e-8d6c-0f1e2d3c4b5a",
                        "direction": "outbound",
                        "subject": "Request for quote: TPS54331DR, 500 units, needed by 30 November 2026",
                        "body": "Hi Tom,\n\nMy name is Jane Smith, Procurement Manager at Acme Corp. We're looking for 500 units of the Texas Instruments TPS54331DR and need them delivered by 30 November 2026. Could you send your best unit price, availability and lead time, along with any minimum order quantity and how long the quote is valid?\n\nThank you,\n\nJane Smith\nAcme Corp",
                        "sent_at": "2026-10-05T16:02:11.307Z",
                        "attachments": [],
                        "reply_kind": null,
                        "quote": null
                      }
                    ]
                  }
                }
              }
            }
          },
          "400": {
            "description": "The body is not valid, `supplier.email` is not an address mail can be sent to, or, with a key made with `POST /v1/keys`, `notify_email` is missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, revoked or expired key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "This request was not run and nothing was charged. The error code names the case. `credits_exhausted`: your plan's credits for this month are used up; upgrade the plan in the PartFinder console. `payment_method_required`: your organization is on pay as you go with no payment method; add one in the console, or choose a plan. `payment_failed`: pay as you go is paused because the last payment failed; update the payment method in the console. `usage_limit_reached`: your organization, on pay as you go, has reached its limit for unpaid usage; calls resume once the current payment goes through. With a key made with `POST /v1/keys`, the `summary` tells your customer to contact your organization, and the answer carries no `billing_url`. The `summary` can be relayed as is; retry once the case is resolved.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "credits_exhausted",
                  "summary": "Your organization's credits for this month are used up, so this request was not run. Upgrade your plan in the PartFinder console at https://console.partfinder.dev/settings/billing, then try again.",
                  "reason": "This request needs 1 credit and 0 are left.",
                  "billing_url": "https://console.partfinder.dev/settings/billing"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "403": {
            "description": "The caller signed in through an AI app without the permission this call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the error is `insufficient_scope`. Or the AI app was disconnected in the console; the error is `app_disconnected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "409": {
            "description": "The supplier asked not to receive requests for quote from you, or from anyone through PartFinder.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "422": {
            "description": "Your `message` was not sent: `summary` says what to change before sending again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "429": {
            "description": "Your organization has used its calls for this minute or this day. Wait the number of seconds in `Retry-After`, then retry.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the limit resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The email text could not be written, or the email could not be sent. Nothing was sent; retry shortly.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listRfqs",
        "tags": [
          "RFQs"
        ],
        "summary": "List your requests for quote",
        "description": "Every request for quote your organization has sent (with a key made with `POST /v1/keys`, only that customer's), newest first, with its status, one page at a time. When `next_cursor` is not null, call again with `cursor` set to it for the next page. Read one with `GET /v1/rfq/{id}` for its messages.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            },
            "description": "Requests for quote per page, 1 to 100. Defaults to 50."
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "`next_cursor` from the previous page, unchanged. Omit it for the first page."
          }
        ],
        "responses": {
          "200": {
            "description": "The list.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RfqList"
                },
                "example": {
                  "as_of": "2026-10-07T09:15:02.118Z",
                  "summary": "2 requests for quote, newest first, 1 with a reply. This is the last page.",
                  "notice": "Informational only. Risk scores, stock levels, lead times and alternate-part suggestions may be incomplete, delayed or inaccurate. Independently verify before making procurement, engineering, compliance, safety or sourcing decisions.",
                  "rfqs": [
                    {
                      "id": "7c1e9a4b-2d3f-4e5a-8b6c-9d0e1f2a3b4c",
                      "status": "bounced",
                      "supplier": {
                        "email": "quotes@distributor.example",
                        "name": "Example Distributor",
                        "contact_name": null
                      },
                      "requester": {
                        "company": "Acme Corp",
                        "contact_name": "Jane Smith",
                        "title": "Procurement Manager"
                      },
                      "parts": [
                        {
                          "mpn": "LM358DR",
                          "manufacturer": null,
                          "quantity": 1000
                        }
                      ],
                      "needed_by": null,
                      "notify_email": null,
                      "created_at": "2026-10-06T08:41:17.204Z",
                      "first_reply_at": null,
                      "follow_up_count": 0,
                      "replies_received": 0,
                      "quotes_received": 0
                    },
                    {
                      "id": "5b0f3c1e-8a2d-4f6b-9c7e-1d2a3b4c5d6e",
                      "status": "replied",
                      "supplier": {
                        "email": "sales@supplier.example",
                        "name": "Example Supplier",
                        "contact_name": "Tom Lee"
                      },
                      "requester": {
                        "company": "Acme Corp",
                        "contact_name": "Jane Smith",
                        "title": "Procurement Manager"
                      },
                      "parts": [
                        {
                          "mpn": "TPS54331DR",
                          "manufacturer": "Texas Instruments",
                          "quantity": 500
                        }
                      ],
                      "needed_by": "2026-11-30",
                      "notify_email": null,
                      "created_at": "2026-10-05T16:02:09.913Z",
                      "first_reply_at": "2026-10-06T14:27:40.551Z",
                      "follow_up_count": 0,
                      "replies_received": 1,
                      "quotes_received": 1
                    }
                  ],
                  "next_cursor": null
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "400": {
            "description": "`limit` is outside 1-100, or `cursor` is not one issued to you.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, revoked or expired key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The caller signed in through an AI app without the permission this call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the error is `insufficient_scope`. Or the AI app was disconnected in the console; the error is `app_disconnected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "429": {
            "description": "Your organization has used its calls for this minute or this day. Wait the number of seconds in `Retry-After`, then retry.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the limit resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/rfq/{id}": {
      "get": {
        "operationId": "getRfq",
        "tags": [
          "RFQs"
        ],
        "summary": "Get a request for quote",
        "description": "A request for quote and its email thread, oldest first: what was sent and every reply from the supplier. `status` is `replied` once the supplier has answered. Poll this to see new replies.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The request for quote id."
          }
        ],
        "responses": {
          "200": {
            "description": "The request for quote.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RfqResponse"
                },
                "example": {
                  "as_of": "2026-10-07T09:15:02.118Z",
                  "summary": "1 reply from sales@supplier.example; the latest is the last inbound message. Answer or follow up with POST /v1/rfq/5b0f3c1e-8a2d-4f6b-9c7e-1d2a3b4c5d6e/messages.",
                  "notice": "Informational only. Risk scores, stock levels, lead times and alternate-part suggestions may be incomplete, delayed or inaccurate. Independently verify before making procurement, engineering, compliance, safety or sourcing decisions.",
                  "rfq": {
                    "id": "5b0f3c1e-8a2d-4f6b-9c7e-1d2a3b4c5d6e",
                    "status": "replied",
                    "supplier": {
                      "email": "sales@supplier.example",
                      "name": "Example Supplier",
                      "contact_name": "Tom Lee"
                    },
                    "requester": {
                      "company": "Acme Corp",
                      "contact_name": "Jane Smith",
                      "title": "Procurement Manager"
                    },
                    "parts": [
                      {
                        "mpn": "TPS54331DR",
                        "manufacturer": "Texas Instruments",
                        "quantity": 500
                      }
                    ],
                    "needed_by": "2026-11-30",
                    "notify_email": null,
                    "created_at": "2026-10-05T16:02:09.913Z",
                    "first_reply_at": "2026-10-06T14:27:40.551Z",
                    "follow_up_count": 0,
                    "replies_received": 1,
                    "quotes_received": 1,
                    "messages": [
                      {
                        "id": "9e4d2c7a-1b3f-4a5e-8d6c-0f1e2d3c4b5a",
                        "direction": "outbound",
                        "subject": "Request for quote: TPS54331DR, 500 units, needed by 30 November 2026",
                        "body": "Hi Tom,\n\nMy name is Jane Smith, Procurement Manager at Acme Corp. We're looking for 500 units of the Texas Instruments TPS54331DR and need them delivered by 30 November 2026. Could you send your best unit price, availability and lead time, along with any minimum order quantity and how long the quote is valid?\n\nThank you,\n\nJane Smith\nAcme Corp",
                        "sent_at": "2026-10-05T16:02:11.307Z",
                        "attachments": [],
                        "reply_kind": null,
                        "quote": null
                      },
                      {
                        "id": "2c8b6e1f-7d3a-4e9b-a1c5-6f0d2e4b8a73",
                        "direction": "inbound",
                        "subject": "Re: Request for quote: TPS54331DR, 500 units, needed by 30 November 2026",
                        "body": "Hi Jane,\n\nWe can offer TPS54331DR at $1.85 each for 500 units, 6 weeks lead time, MOQ 250. Quote valid for 30 days.\n\nBest regards,\nTom Lee\nExample Supplier",
                        "sent_at": "2026-10-06T14:27:40.551Z",
                        "attachments": [],
                        "reply_kind": "quote",
                        "quote": {
                          "unit_price": {
                            "value": 1.85,
                            "words": "$1.85 each",
                            "reason": null
                          },
                          "currency": {
                            "value": null,
                            "words": "$1.85 each",
                            "reason": "The reply gives \"$\" without saying which dollar."
                          },
                          "quantity": {
                            "value": 500,
                            "words": "500 units",
                            "reason": null
                          },
                          "lead_time_days": {
                            "value": 42,
                            "words": "6 weeks",
                            "reason": null
                          },
                          "moq": {
                            "value": 250,
                            "words": "MOQ 250",
                            "reason": null
                          }
                        }
                      }
                    ]
                  }
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, revoked or expired key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The caller signed in through an AI app without the permission this call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the error is `insufficient_scope`. Or the AI app was disconnected in the console; the error is `app_disconnected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "404": {
            "description": "No request for quote with that id belongs to your organization (with a key made with `POST /v1/keys`, to that customer).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "429": {
            "description": "Your organization has used its calls for this minute or this day. Wait the number of seconds in `Retry-After`, then retry.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the limit resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteRfq",
        "tags": [
          "RFQs"
        ],
        "summary": "Delete a request for quote",
        "description": "Removes the request for quote and every message in its thread. Irreversible. A reply the supplier sends afterwards is not kept, and the supplier is not told.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The request for quote id."
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RfqDeleted"
                },
                "example": {
                  "as_of": "2026-10-07T09:31:08.214Z",
                  "summary": "Request for quote 5b0f3c1e-8a2d-4f6b-9c7e-1d2a3b4c5d6e and its messages were deleted. A reply the supplier sends to it later is not kept.",
                  "notice": "Informational only. Risk scores, stock levels, lead times and alternate-part suggestions may be incomplete, delayed or inaccurate. Independently verify before making procurement, engineering, compliance, safety or sourcing decisions.",
                  "deleted": {
                    "id": "5b0f3c1e-8a2d-4f6b-9c7e-1d2a3b4c5d6e"
                  }
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, revoked or expired key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The caller signed in through an AI app without the permission this call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the error is `insufficient_scope`. Or the AI app was disconnected in the console; the error is `app_disconnected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "404": {
            "description": "No request for quote with that id belongs to your organization (with a key made with `POST /v1/keys`, to that customer).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "429": {
            "description": "Your organization has used its calls for this minute or this day. Wait the number of seconds in `Retry-After`, then retry.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the limit resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/rfq/{id}/messages": {
      "post": {
        "operationId": "sendRfqMessage",
        "tags": [
          "RFQs"
        ],
        "summary": "Send a message in a request for quote",
        "description": "Answer the supplier, or send a reminder, in the same email thread as the request for quote. The text is sent as written, under the requester's name.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The request for quote id."
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "example": "3d9a7f2e-6c41-4b8e-a5d0-7e2f19c4b863",
            "description": "Set by your client code, not by a person or a model: generate a new value (a UUID) for each message, and send the same value again only when retrying that same message after a timeout. A retry with the same key on the same request for quote sends nothing and returns the request for quote with the message the first call sent. A key is recognised only from the same API key that sent it. Optional."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RfqMessageRequest"
              },
              "example": {
                "body": "Hi Tom,\n\nThank you. Can you ship to Singapore, and does the price hold for 1,000 units?\n\nBest regards,\nJane Smith\nAcme Corp"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Sent. The body is the request for quote with the new message last.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RfqResponse"
                },
                "example": {
                  "as_of": "2026-10-07T09:20:44.903Z",
                  "summary": "Sent to sales@supplier.example in the same thread. Replies appear on GET /v1/rfq/5b0f3c1e-8a2d-4f6b-9c7e-1d2a3b4c5d6e.",
                  "notice": "Informational only. Risk scores, stock levels, lead times and alternate-part suggestions may be incomplete, delayed or inaccurate. Independently verify before making procurement, engineering, compliance, safety or sourcing decisions.",
                  "rfq": {
                    "id": "5b0f3c1e-8a2d-4f6b-9c7e-1d2a3b4c5d6e",
                    "status": "replied",
                    "supplier": {
                      "email": "sales@supplier.example",
                      "name": "Example Supplier",
                      "contact_name": "Tom Lee"
                    },
                    "requester": {
                      "company": "Acme Corp",
                      "contact_name": "Jane Smith",
                      "title": "Procurement Manager"
                    },
                    "parts": [
                      {
                        "mpn": "TPS54331DR",
                        "manufacturer": "Texas Instruments",
                        "quantity": 500
                      }
                    ],
                    "needed_by": "2026-11-30",
                    "notify_email": null,
                    "created_at": "2026-10-05T16:02:09.913Z",
                    "first_reply_at": "2026-10-06T14:27:40.551Z",
                    "follow_up_count": 1,
                    "replies_received": 1,
                    "quotes_received": 1,
                    "messages": [
                      {
                        "id": "9e4d2c7a-1b3f-4a5e-8d6c-0f1e2d3c4b5a",
                        "direction": "outbound",
                        "subject": "Request for quote: TPS54331DR, 500 units, needed by 30 November 2026",
                        "body": "Hi Tom,\n\nMy name is Jane Smith, Procurement Manager at Acme Corp. We're looking for 500 units of the Texas Instruments TPS54331DR and need them delivered by 30 November 2026. Could you send your best unit price, availability and lead time, along with any minimum order quantity and how long the quote is valid?\n\nThank you,\n\nJane Smith\nAcme Corp",
                        "sent_at": "2026-10-05T16:02:11.307Z",
                        "attachments": [],
                        "reply_kind": null,
                        "quote": null
                      },
                      {
                        "id": "2c8b6e1f-7d3a-4e9b-a1c5-6f0d2e4b8a73",
                        "direction": "inbound",
                        "subject": "Re: Request for quote: TPS54331DR, 500 units, needed by 30 November 2026",
                        "body": "Hi Jane,\n\nWe can offer TPS54331DR at $1.85 each for 500 units, 6 weeks lead time, MOQ 250. Quote valid for 30 days.\n\nBest regards,\nTom Lee\nExample Supplier",
                        "sent_at": "2026-10-06T14:27:40.551Z",
                        "attachments": [],
                        "reply_kind": "quote",
                        "quote": {
                          "unit_price": {
                            "value": 1.85,
                            "words": "$1.85 each",
                            "reason": null
                          },
                          "currency": {
                            "value": null,
                            "words": "$1.85 each",
                            "reason": "The reply gives \"$\" without saying which dollar."
                          },
                          "quantity": {
                            "value": 500,
                            "words": "500 units",
                            "reason": null
                          },
                          "lead_time_days": {
                            "value": 42,
                            "words": "6 weeks",
                            "reason": null
                          },
                          "moq": {
                            "value": 250,
                            "words": "MOQ 250",
                            "reason": null
                          }
                        }
                      },
                      {
                        "id": "7a1e9d3c-5b2f-4c8a-9e6d-3f1b0c2a4d58",
                        "direction": "outbound",
                        "subject": "Re: Request for quote: TPS54331DR, 500 units, needed by 30 November 2026",
                        "body": "Hi Tom,\n\nThank you. Can you ship to Singapore, and does the price hold for 1,000 units?\n\nBest regards,\nJane Smith\nAcme Corp",
                        "sent_at": "2026-10-07T09:20:44.512Z",
                        "attachments": [],
                        "reply_kind": null,
                        "quote": null
                      }
                    ]
                  }
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "400": {
            "description": "The body is not valid: `body` is missing or empty.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, revoked or expired key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "This request was not run and nothing was charged. The error code names the case. `credits_exhausted`: your plan's credits for this month are used up; upgrade the plan in the PartFinder console. `payment_method_required`: your organization is on pay as you go with no payment method; add one in the console, or choose a plan. `payment_failed`: pay as you go is paused because the last payment failed; update the payment method in the console. `usage_limit_reached`: your organization, on pay as you go, has reached its limit for unpaid usage; calls resume once the current payment goes through. With a key made with `POST /v1/keys`, the `summary` tells your customer to contact your organization, and the answer carries no `billing_url`. The `summary` can be relayed as is; retry once the case is resolved.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "credits_exhausted",
                  "summary": "Your organization's credits for this month are used up, so this request was not run. Upgrade your plan in the PartFinder console at https://console.partfinder.dev/settings/billing, then try again.",
                  "reason": "This request needs 1 credit and 0 are left.",
                  "billing_url": "https://console.partfinder.dev/settings/billing"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "403": {
            "description": "The caller signed in through an AI app without the permission this call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the error is `insufficient_scope`. Or the AI app was disconnected in the console; the error is `app_disconnected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "404": {
            "description": "No request for quote with that id belongs to your organization (with a key made with `POST /v1/keys`, to that customer).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "409": {
            "description": "The supplier's address bounced, or the supplier asked not to receive requests for quote.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "422": {
            "description": "The message was not sent: `summary` says what to change before sending again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "429": {
            "description": "Your organization has used its calls for this minute or this day. Wait the number of seconds in `Retry-After`, then retry.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the limit resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The message could not be sent. Nothing was sent; retry shortly.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          }
        }
      }
    },
    "/v1/keys": {
      "post": {
        "operationId": "createEndCustomerKey",
        "tags": [
          "Keys"
        ],
        "summary": "Create a key for a customer",
        "description": "Create an API key for one of your own customers. Requests for quote, BOMs, searches and comparisons made with it belong to that customer: its keys see only those, while your organization's own keys see everything. Its calls count against your organization's rate limit and its credits too. The response carries the key's secret, the only time it is shown. Send `company` for a new customer, or `end_customer_id` to add a key to one you already created.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KeyRequest"
              },
              "example": {
                "company": "Acme Corp"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created. Store `secret` now: it is not shown again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KeyCreated"
                },
                "example": {
                  "as_of": "2026-10-07T10:12:40.733Z",
                  "summary": "Key created for Acme Corp. It sees only this customer's requests for quote, BOMs, searches and comparisons. Give the secret to your customer now: it is not shown again.",
                  "notice": "Informational only. Risk scores, stock levels, lead times and alternate-part suggestions may be incomplete, delayed or inaccurate. Independently verify before making procurement, engineering, compliance, safety or sourcing decisions.",
                  "key": {
                    "id": "ak_2c4e6a8b0d1f3e5a7c9b1d3f5e7a9c0b",
                    "name": "Acme Corp 3f9a1c",
                    "end_customer_id": "7d2e4c1a-5b3f-4e8d-9a6c-2f1b0e3d4c5a",
                    "company": "Acme Corp",
                    "created_at": "2026-10-07T10:12:40.512Z"
                  },
                  "secret": "ak_live_example_shown_once"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "400": {
            "description": "Neither or both of `company` and `end_customer_id`, or an `end_customer_id` your organization never created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, revoked or expired key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key belongs to one of your customers, or the caller signed in through an AI app; only your organization's own keys manage keys. The caller signed in through an AI app without the permission this call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the error is `insufficient_scope`. Or the AI app was disconnected in the console; the error is `app_disconnected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "409": {
            "description": "A key with that `name` already exists in your organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "429": {
            "description": "Your organization has used its calls for this minute or this day. Wait the number of seconds in `Retry-After`, then retry.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the limit resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listEndCustomerKeys",
        "tags": [
          "Keys"
        ],
        "summary": "List customer keys",
        "description": "Every key your organization created for its customers and has not revoked, newest first. Secrets are never listed.",
        "responses": {
          "200": {
            "description": "The keys.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KeyList"
                },
                "example": {
                  "as_of": "2026-10-07T10:14:02.091Z",
                  "summary": "1 key for 1 end customer.",
                  "notice": "Informational only. Risk scores, stock levels, lead times and alternate-part suggestions may be incomplete, delayed or inaccurate. Independently verify before making procurement, engineering, compliance, safety or sourcing decisions.",
                  "keys": [
                    {
                      "id": "ak_2c4e6a8b0d1f3e5a7c9b1d3f5e7a9c0b",
                      "name": "Acme Corp 3f9a1c",
                      "end_customer_id": "7d2e4c1a-5b3f-4e8d-9a6c-2f1b0e3d4c5a",
                      "company": "Acme Corp",
                      "created_at": "2026-10-07T10:12:40.512Z"
                    }
                  ]
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, revoked or expired key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key belongs to one of your customers, or the caller signed in through an AI app; only your organization's own keys manage keys. The caller signed in through an AI app without the permission this call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the error is `insufficient_scope`. Or the AI app was disconnected in the console; the error is `app_disconnected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "429": {
            "description": "Your organization has used its calls for this minute or this day. Wait the number of seconds in `Retry-After`, then retry.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the limit resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/keys/{id}": {
      "delete": {
        "operationId": "revokeEndCustomerKey",
        "tags": [
          "Keys"
        ],
        "summary": "Revoke a customer key",
        "description": "Revoke one key of one of your customers. It stops working within about a minute. The customer's requests for quote, BOMs, searches and comparisons stay, and its other keys keep working. Your organization's own keys are not managed here.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The key id."
          }
        ],
        "responses": {
          "200": {
            "description": "Revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KeyRevoked"
                },
                "example": {
                  "as_of": "2026-10-07T10:20:17.384Z",
                  "summary": "Key ak_2c4e6a8b0d1f3e5a7c9b1d3f5e7a9c0b was revoked. It stops working within about a minute; the end customer's data and other keys are unchanged.",
                  "notice": "Informational only. Risk scores, stock levels, lead times and alternate-part suggestions may be incomplete, delayed or inaccurate. Independently verify before making procurement, engineering, compliance, safety or sourcing decisions.",
                  "revoked": {
                    "id": "ak_2c4e6a8b0d1f3e5a7c9b1d3f5e7a9c0b"
                  }
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, revoked or expired key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key belongs to one of your customers, or the caller signed in through an AI app; only your organization's own keys manage keys. The caller signed in through an AI app without the permission this call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the error is `insufficient_scope`. Or the AI app was disconnected in the console; the error is `app_disconnected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "404": {
            "description": "No live customer key with that id belongs to your organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "429": {
            "description": "Your organization has used its calls for this minute or this day. Wait the number of seconds in `Retry-After`, then retry.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the limit resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhooks": {
      "post": {
        "operationId": "createWebhookEndpoint",
        "tags": [
          "Webhooks"
        ],
        "summary": "Register a webhook endpoint",
        "description": "Register an https:// address on your server. Each time a supplier replies to one of your requests for quote, an address bounces, or a search or a comparison ends, we send that address a signed event naming the request, the search or the comparison, so you read it with `GET /v1/rfq/{id}`, `GET /v1/search/{id}` or `GET /v1/compare/{id}` instead of polling. The response carries the endpoint's signing secret, the only time it is shown: store it, and check the `webhook-signature` header of every event with it. An endpoint registered with one of your organization's own keys receives every event of your organization; one registered with a key you created for one of your customers (`POST /v1/keys`) receives only that customer's events.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEndpointRequest"
              },
              "example": {
                "url": "https://api.example.com/hooks/partfinder"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Registered. Store `secret` now: it is not shown again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointCreated"
                },
                "example": {
                  "as_of": "2026-10-07T09:20:11.455Z",
                  "summary": "Events for your requests for quote, searches and comparisons will be sent to https://api.example.com/hooks/partfinder. Store the secret now: it is not shown again.",
                  "notice": "Informational only. Risk scores, stock levels, lead times and alternate-part suggestions may be incomplete, delayed or inaccurate. Independently verify before making procurement, engineering, compliance, safety or sourcing decisions.",
                  "endpoint": {
                    "id": "9c41e7a2-5d3b-4f0e-8a16-2b7d94c0e5f1",
                    "url": "https://api.example.com/hooks/partfinder",
                    "created_at": "2026-10-07T09:20:11.402Z"
                  },
                  "secret": "whsec_EXAMPLEdoNOTuseEXAMPLEdoNOTuseEXAMPLEdoNOTuse0="
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "400": {
            "description": "`url` is missing, not an https:// URL, or not on the public internet.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, revoked or expired key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The caller signed in through an AI app; webhooks are managed with your organization's API keys. The caller signed in through an AI app without the permission this call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the error is `insufficient_scope`. Or the AI app was disconnected in the console; the error is `app_disconnected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "429": {
            "description": "Your organization has used its calls for this minute or this day. Wait the number of seconds in `Retry-After`, then retry.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the limit resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listWebhookEndpoints",
        "tags": [
          "Webhooks"
        ],
        "summary": "List webhook endpoints",
        "description": "Every endpoint your organization registered, newest first; with a key made with `POST /v1/keys`, only the endpoints registered with that customer's keys. Secrets are never listed.",
        "responses": {
          "200": {
            "description": "The endpoints.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointList"
                },
                "example": {
                  "as_of": "2026-10-07T09:21:30.017Z",
                  "summary": "1 webhook endpoint registered.",
                  "notice": "Informational only. Risk scores, stock levels, lead times and alternate-part suggestions may be incomplete, delayed or inaccurate. Independently verify before making procurement, engineering, compliance, safety or sourcing decisions.",
                  "endpoints": [
                    {
                      "id": "9c41e7a2-5d3b-4f0e-8a16-2b7d94c0e5f1",
                      "url": "https://api.example.com/hooks/partfinder",
                      "created_at": "2026-10-07T09:20:11.402Z"
                    }
                  ]
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, revoked or expired key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The caller signed in through an AI app; webhooks are managed with your organization's API keys. The caller signed in through an AI app without the permission this call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the error is `insufficient_scope`. Or the AI app was disconnected in the console; the error is `app_disconnected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "429": {
            "description": "Your organization has used its calls for this minute or this day. Wait the number of seconds in `Retry-After`, then retry.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the limit resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhooks/{id}": {
      "delete": {
        "operationId": "deleteWebhookEndpoint",
        "tags": [
          "Webhooks"
        ],
        "summary": "Delete a webhook endpoint",
        "description": "Stops sending events to the endpoint. Irreversible; register it again for a new secret. With a key made with `POST /v1/keys`, only that customer's endpoints can be deleted; any other id answers `404`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The endpoint id."
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointDeleted"
                },
                "example": {
                  "as_of": "2026-10-07T09:22:04.880Z",
                  "summary": "Webhook endpoint 9c41e7a2-5d3b-4f0e-8a16-2b7d94c0e5f1 was deleted. No more events are sent to it.",
                  "notice": "Informational only. Risk scores, stock levels, lead times and alternate-part suggestions may be incomplete, delayed or inaccurate. Independently verify before making procurement, engineering, compliance, safety or sourcing decisions.",
                  "deleted": {
                    "id": "9c41e7a2-5d3b-4f0e-8a16-2b7d94c0e5f1"
                  }
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, revoked or expired key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The caller signed in through an AI app; webhooks are managed with your organization's API keys. The caller signed in through an AI app without the permission this call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the error is `insufficient_scope`. Or the AI app was disconnected in the console; the error is `app_disconnected`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "404": {
            "description": "No webhook endpoint with that id belongs to your organization (with a key made with `POST /v1/keys`, to that customer).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "429": {
            "description": "Your organization has used its calls for this minute or this day. Wait the number of seconds in `Retry-After`, then retry.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the limit resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Limit": {
                "description": "Calls allowed in the current window: the minute or the day, whichever has fewer calls left.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Calls left in that window.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Seconds until that window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "An API key created in the PartFinder console at https://console.partfinder.dev, or a customer key created with `POST /v1/keys`. Send as `Authorization: Bearer <key>`. Revocation takes effect within approximately one minute."
      }
    },
    "schemas": {
      "Alternate": {
        "type": "object",
        "properties": {
          "mpn": {
            "type": "string",
            "description": "The alternate's manufacturer part number."
          },
          "manufacturer": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "description": {
            "description": "A one-line description of the part. Null when none is available.",
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "lifecycle": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "Active",
                  "Mature",
                  "NRND",
                  "EOL",
                  "Obsolete"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "Where the part is in its life. Null when no source states one."
          },
          "match": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "direct",
                  "parametric_equivalent",
                  "similar",
                  "upgrade"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "How a supplier catalogue classifies this part against the one you asked about: `direct`, `parametric_equivalent`, `similar` or `upgrade`. Null when the catalogue gives no classification. Confirm fit and specifications against both datasheets before substituting."
          },
          "supply": {
            "type": "object",
            "properties": {
              "stock": {
                "description": "Units held across the distributors we can see.",
                "$ref": "#/components/schemas/Measure"
              },
              "distributor_count": {
                "anyOf": [
                  {
                    "type": "integer",
                    "minimum": 0
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Distributors holding stock. Null when supply could not be read; `stock` says why."
              },
              "lead_time_median": {
                "$ref": "#/components/schemas/Measure"
              }
            },
            "required": [
              "stock",
              "distributor_count",
              "lead_time_median"
            ]
          }
        },
        "required": [
          "mpn",
          "manufacturer",
          "description",
          "lifecycle",
          "match",
          "supply"
        ]
      },
      "AlternatesReport": {
        "type": "object",
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601. How current the data in this response is."
          },
          "summary": {
            "type": "string",
            "description": "Plain language an agent can relay without re-deriving it."
          },
          "notice": {
            "type": "string",
            "description": "Informational-use notice. Always present."
          },
          "part": {
            "type": "object",
            "properties": {
              "mpn": {
                "type": "string",
                "description": "The part number you sent."
              },
              "manufacturer": {
                "description": "The `manufacturer` you sent, or null.",
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "mpn",
              "manufacturer"
            ]
          },
          "alternates": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Alternate"
            },
            "description": "Parts that could replace this one, up to five. Never this part itself or the same part in another package or ordering variant. Not ranked: the order carries no meaning, so compare them on `match`, `supply` and the datasheets. Empty when none was found."
          }
        },
        "required": [
          "as_of",
          "summary",
          "notice",
          "part",
          "alternates"
        ]
      },
      "BomAccepted": {
        "type": "object",
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601. How current the data in this response is."
          },
          "summary": {
            "type": "string",
            "description": "Plain language an agent can relay without re-deriving it."
          },
          "notice": {
            "type": "string",
            "description": "Informational-use notice. Always present."
          },
          "bom": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "description": "The BOM id. Use it with `GET /v1/bom/{id}` and `DELETE /v1/bom/{id}`."
              },
              "name": {
                "type": "string"
              },
              "status": {
                "type": "string",
                "enum": [
                  "processing",
                  "complete"
                ],
                "description": "`processing` until every line has settled (identified or not); then `complete`."
              },
              "lines": {
                "type": "integer",
                "minimum": 0,
                "description": "Lines accepted."
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              }
            },
            "required": [
              "id",
              "name",
              "status",
              "lines",
              "created_at"
            ]
          }
        },
        "required": [
          "as_of",
          "summary",
          "notice",
          "bom"
        ]
      },
      "BomDeleted": {
        "type": "object",
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601. How current the data in this response is."
          },
          "summary": {
            "type": "string",
            "description": "Plain language an agent can relay without re-deriving it."
          },
          "notice": {
            "type": "string",
            "description": "Informational-use notice. Always present."
          },
          "deleted": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              }
            },
            "required": [
              "id"
            ]
          }
        },
        "required": [
          "as_of",
          "summary",
          "notice",
          "deleted"
        ]
      },
      "BomLine": {
        "type": "object",
        "properties": {
          "line": {
            "type": "integer",
            "minimum": 1,
            "description": "Position in the BOM as submitted, starting at 1."
          },
          "mpn": {
            "type": "string",
            "description": "The part number as submitted."
          },
          "manufacturer": {
            "description": "As submitted, or as identified when the BOM did not name one.",
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "quantity": {
            "anyOf": [
              {
                "type": "integer",
                "exclusiveMinimum": 0
              },
              {
                "type": "null"
              }
            ],
            "description": "Units of this line in the build, as submitted. Null when none was given."
          },
          "status": {
            "type": "string",
            "enum": [
              "resolved",
              "low_confidence",
              "unresolved",
              "pending"
            ],
            "description": "`resolved` when the part was identified with confidence, `low_confidence` when identified tentatively, `unresolved` when it could not be identified, `pending` while it is still being worked."
          },
          "notes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DataNote"
            },
            "description": "Where supplier catalogues disagree with what was submitted for this line. The submitted values are kept as they are. Empty when there is nothing to note."
          },
          "part": {
            "type": "object",
            "properties": {
              "identified_mpn": {
                "description": "The part number as identified, when it differs from the one submitted.",
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "category": {
                "description": "The broad class of part, e.g. \"Semiconductors\". `GET /v1/parts` gives the specific type, e.g. \"DC-DC Buck Converter\".",
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "lifecycle": {
                "anyOf": [
                  {
                    "type": "string",
                    "enum": [
                      "Active",
                      "Mature",
                      "NRND",
                      "EOL",
                      "Obsolete"
                    ]
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "manufacturing_country": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "manufacturing_country_code": {
                "description": "ISO 3166-1 alpha-2 code of `manufacturing_country`. Kosovo is `XK`, the code in common use for it.",
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "identified_mpn",
              "category",
              "lifecycle",
              "manufacturing_country",
              "manufacturing_country_code"
            ]
          },
          "risk": {
            "type": "object",
            "properties": {
              "composite": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/Score"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Combined from the drivers below. Null when the line could not be identified, or when no distributor has stock or lead-time figures for it; `reason` says which."
              },
              "tier": {
                "anyOf": [
                  {
                    "type": "string",
                    "enum": [
                      "low",
                      "medium",
                      "high",
                      "critical"
                    ]
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "The band `composite` falls in. Null when composite is null."
              },
              "reason": {
                "description": "Present only when composite is null.",
                "type": "string"
              },
              "drivers": {
                "type": "object",
                "properties": {
                  "geography": {
                    "anyOf": [
                      {
                        "$ref": "#/components/schemas/DriverBand"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "description": "Risk carried by the country the part is made in. Null when the origin is not known."
                  },
                  "disruption_exposure": {
                    "anyOf": [
                      {
                        "$ref": "#/components/schemas/DriverBand"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "description": "Exposure to active disruption events touching this line's category or origin."
                  },
                  "lead_time_volatility": {
                    "anyOf": [
                      {
                        "$ref": "#/components/schemas/DriverBand"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "description": "Instability of quoted lead times. Null when no lead time is quoted."
                  },
                  "gaps": {
                    "anyOf": [
                      {
                        "$ref": "#/components/schemas/DriverBand"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "description": "Weaknesses in how this BOM sources the line; the gaps found are listed in `gap_flags`."
                  }
                },
                "required": [
                  "geography",
                  "disruption_exposure",
                  "lead_time_volatility",
                  "gaps"
                ],
                "description": "What drives this line's score, each as a band and a sentence. Null when the driver has no data."
              },
              "gap_flags": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "single_source",
                    "no_supplier",
                    "concentration_risk",
                    "high_risk_region",
                    "lifecycle_risk"
                  ],
                  "description": "`single_source`: the only manufacturer for its category in this BOM. `no_supplier`: no supplier named on the line; it counts in the gaps driver. `concentration_risk`: a large share of the BOM is made in one country. `high_risk_region`: made in a high-exposure country. `lifecycle_risk`: end of life, obsolete, or not recommended for new designs."
                }
              },
              "events": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "kind": {
                      "type": "string",
                      "enum": [
                        "news",
                        "port_activity",
                        "sea_route"
                      ],
                      "description": "`news`: a reported disruption. `port_activity`: ship traffic at the ports of a country this line is made in or sold from is well under its usual level. `sea_route`: the same for a sea passage this line's trade uses."
                    },
                    "id": {
                      "anyOf": [
                        {
                          "type": "integer",
                          "minimum": 0
                        },
                        {
                          "type": "null"
                        }
                      ],
                      "description": "The event id for `news`; null otherwise."
                    },
                    "title": {
                      "type": "string"
                    },
                    "root_cause": {
                      "anyOf": [
                        {
                          "type": "string",
                          "enum": [
                            "natural_disaster",
                            "geopolitical_action",
                            "regulatory_change",
                            "labor_disruption",
                            "industrial_incident",
                            "infrastructure_failure",
                            "cyber_attack"
                          ]
                        },
                        {
                          "type": "null"
                        }
                      ],
                      "description": "What kind of cause lies behind a `news` event, as on `GET /v1/events`. Null otherwise."
                    },
                    "country": {
                      "anyOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "severity": {
                      "anyOf": [
                        {
                          "type": "string",
                          "enum": [
                            "low",
                            "medium",
                            "high",
                            "critical"
                          ]
                        },
                        {
                          "type": "null"
                        }
                      ],
                      "description": "For `news`: how serious the event is in itself, not its effect on this line. Null for `port_activity` and `sea_route`, whose `title` states the slowdown."
                    },
                    "published_date": {
                      "anyOf": [
                        {
                          "type": "string",
                          "format": "date-time"
                        },
                        {
                          "type": "null"
                        }
                      ],
                      "description": "For `news`, when it was reported; otherwise the last day of the week it covers."
                    },
                    "url": {
                      "anyOf": [
                        {
                          "type": "string",
                          "format": "uri"
                        },
                        {
                          "type": "null"
                        }
                      ],
                      "description": "A source article for `news`; may be null. Always null otherwise."
                    }
                  },
                  "required": [
                    "kind",
                    "id",
                    "title",
                    "root_cause",
                    "country",
                    "severity",
                    "published_date",
                    "url"
                  ]
                },
                "description": "The current shipping conditions and up to five reported events that most affect this line's `disruption_exposure`, strongest first: ordered by effect on this line's score, not by `severity`."
              }
            },
            "required": [
              "composite",
              "tier",
              "drivers",
              "gap_flags",
              "events"
            ],
            "description": "This line's risk within this BOM, which includes how the rest of the BOM sources it. `GET /v1/parts` scores a part on its own, with different drivers, so the same part can score differently on the two endpoints."
          },
          "supply": {
            "type": "object",
            "properties": {
              "stock": {
                "$ref": "#/components/schemas/Measure"
              },
              "lead_time_median": {
                "$ref": "#/components/schemas/Measure"
              },
              "lead_time_min": {
                "description": "The 25th percentile of quoted lead times: the short end of the middle half of quotes. Null when no distributor quotes a lead time, or when the middle half of quotes is a single figure; `reason` says which.",
                "$ref": "#/components/schemas/Measure"
              },
              "lead_time_max": {
                "description": "The 75th percentile of quoted lead times: the long end of the middle half of quotes. Null when no distributor quotes a lead time, or when the middle half of quotes is a single figure; `reason` says which.",
                "$ref": "#/components/schemas/Measure"
              },
              "distributor_count": {
                "type": "integer",
                "minimum": 0,
                "description": "Distributors holding stock of this part. `distributors` shows only some of them."
              },
              "scope": {
                "anyOf": [
                  {
                    "type": "string",
                    "enum": [
                      "manufacturer",
                      "all_manufacturers",
                      "other_manufacturers"
                    ]
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Whose listings the figures above count. `manufacturer`: this line's manufacturer, the one submitted or, when none was, the one identified for the part number. `all_manufacturers`: no manufacturer was submitted or identified, so every maker of this part number. `other_manufacturers`: none is listed for the named manufacturer, so these are other makers of the same part number. Null when there are no figures."
              },
              "distributors": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Distributor"
                },
                "description": "The distributors holding the most stock, largest first. Not every distributor is listed: `distributor_count` is the total. Rows can share the same inventory, so do not add them up; `stock` is the total."
              }
            },
            "required": [
              "stock",
              "lead_time_median",
              "lead_time_min",
              "lead_time_max",
              "distributor_count",
              "scope",
              "distributors"
            ]
          }
        },
        "required": [
          "line",
          "mpn",
          "manufacturer",
          "quantity",
          "status",
          "notes",
          "part",
          "risk",
          "supply"
        ]
      },
      "BomList": {
        "type": "object",
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601. How current the data in this response is."
          },
          "summary": {
            "type": "string",
            "description": "Plain language an agent can relay without re-deriving it."
          },
          "notice": {
            "type": "string",
            "description": "Informational-use notice. Always present."
          },
          "boms": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "description": "The BOM id. Use it with `GET /v1/bom/{id}` and `DELETE /v1/bom/{id}`."
                },
                "name": {
                  "type": "string"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "processing",
                    "complete"
                  ],
                  "description": "`processing` until every line has settled (identified or not); then `complete`."
                },
                "lines": {
                  "type": "integer",
                  "minimum": 0,
                  "description": "Lines accepted."
                },
                "created_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "completed_at": {
                  "anyOf": [
                    {
                      "type": "string",
                      "format": "date-time"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "When every line settled. Null while processing."
                }
              },
              "required": [
                "id",
                "name",
                "status",
                "lines",
                "created_at",
                "completed_at"
              ]
            },
            "description": "One page, newest first."
          },
          "next_cursor": {
            "description": "Pass as `cursor` to get the next page. Null when this page is the last.",
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "as_of",
          "summary",
          "notice",
          "boms",
          "next_cursor"
        ]
      },
      "BomReport": {
        "type": "object",
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601. How current the data in this response is."
          },
          "summary": {
            "type": "string",
            "description": "Plain language an agent can relay without re-deriving it."
          },
          "notice": {
            "type": "string",
            "description": "Informational-use notice. Always present."
          },
          "bom": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "description": "The BOM id. Use it with `GET /v1/bom/{id}` and `DELETE /v1/bom/{id}`."
              },
              "name": {
                "type": "string"
              },
              "status": {
                "type": "string",
                "enum": [
                  "processing",
                  "complete"
                ],
                "description": "`processing` until every line has settled (identified or not); then `complete`."
              },
              "lines": {
                "type": "integer",
                "minimum": 0,
                "description": "Lines accepted."
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              },
              "completed_at": {
                "anyOf": [
                  {
                    "type": "string",
                    "format": "date-time"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "When every line settled. Null while processing."
              },
              "identified": {
                "type": "integer",
                "minimum": 0,
                "description": "Lines identified, `resolved` and `low_confidence` together."
              },
              "unresolved": {
                "type": "integer",
                "minimum": 0,
                "description": "Lines that could not be identified."
              }
            },
            "required": [
              "id",
              "name",
              "status",
              "lines",
              "created_at",
              "completed_at",
              "identified",
              "unresolved"
            ]
          },
          "scored_for_country": {
            "description": "The buyer country the scores are computed for, from the `country` parameter. Null when no country was sent: the scores are then not tied to any buyer location.",
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "risk": {
            "type": "object",
            "properties": {
              "average": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/Score"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "The mean of the scored lines. Null while no line is scored."
              },
              "distribution": {
                "type": "object",
                "properties": {
                  "critical": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "high": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "medium": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "low": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "not_scored": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Lines with no score: unresolved, pending, or identified without enough data."
                  }
                },
                "required": [
                  "critical",
                  "high",
                  "medium",
                  "low",
                  "not_scored"
                ]
              },
              "gaps": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/GapTally"
                },
                "description": "How many lines carry each gap. Only kinds present in this BOM are listed."
              }
            },
            "required": [
              "average",
              "distribution",
              "gaps"
            ]
          },
          "countries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CountryShare"
            },
            "description": "Where the BOM is made, most lines first. Lines with no known origin are not listed."
          },
          "parts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BomLine"
            },
            "description": "Every line, in the order submitted. Unresolved lines are present with nulls."
          }
        },
        "required": [
          "as_of",
          "summary",
          "notice",
          "bom",
          "scored_for_country",
          "risk",
          "countries",
          "parts"
        ]
      },
      "BomRequest": {
        "type": "object",
        "properties": {
          "name": {
            "description": "A label for this BOM, shown back in `GET /v1/bom`.",
            "type": "string",
            "maxLength": 200
          },
          "parts": {
            "minItems": 1,
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "mpn": {
                  "type": "string",
                  "minLength": 1,
                  "description": "The manufacturer part number. Required."
                },
                "manufacturer": {
                  "description": "The manufacturer, when the BOM names one.",
                  "anyOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "quantity": {
                  "description": "Units of this line in the build.",
                  "anyOf": [
                    {
                      "type": "integer",
                      "exclusiveMinimum": 0
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "supplier": {
                  "description": "Who you buy this line from, when the BOM names one. A line with no supplier is flagged `no_supplier`.",
                  "anyOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "description": {
                  "description": "The line description from the BOM, when there is one.",
                  "anyOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ]
                }
              },
              "required": [
                "mpn"
              ]
            },
            "maxItems": 500,
            "description": "The BOM lines, up to 500 per call; more answers `422 bom_too_large`. A file uploaded to `POST /v1/bom/upload` carries up to 2,000 rows in one BOM."
          }
        },
        "required": [
          "parts"
        ]
      },
      "Compare": {
        "type": "object",
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601. How current the data in this response is."
          },
          "summary": {
            "type": "string",
            "description": "Plain language an agent can relay without re-deriving it."
          },
          "notice": {
            "type": "string",
            "description": "Informational-use notice. Always present."
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "processing",
              "complete",
              "failed"
            ],
            "description": "`processing` until the comparison ends; `complete` with its parts; `failed` when it could not be completed (submit it again)."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "completed_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "When the comparison ended. Null while processing."
          },
          "columns": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The specification names every part's `specs` and `verdicts` use. Empty until the comparison is complete, and also when the reference has nothing to be compared with."
          },
          "parts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ComparePart"
            },
            "description": "The reference first, then the parts compared with it. Empty until the comparison is complete; a completed comparison of a part with no alternates carries the reference alone."
          }
        },
        "required": [
          "as_of",
          "summary",
          "notice",
          "id",
          "status",
          "created_at",
          "completed_at",
          "columns",
          "parts"
        ]
      },
      "CompareAccepted": {
        "type": "object",
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601. How current the data in this response is."
          },
          "summary": {
            "type": "string",
            "description": "Plain language an agent can relay without re-deriving it."
          },
          "notice": {
            "type": "string",
            "description": "Informational-use notice. Always present."
          },
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Poll `GET /v1/compare/{id}` with it."
          },
          "status": {
            "type": "string",
            "enum": [
              "processing",
              "complete",
              "failed"
            ],
            "description": "`processing` for a new comparison. A retry with an `Idempotency-Key` already used returns the comparison that key started, with its current status."
          }
        },
        "required": [
          "as_of",
          "summary",
          "notice",
          "id",
          "status"
        ]
      },
      "CompareDeleted": {
        "type": "object",
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601. How current the data in this response is."
          },
          "summary": {
            "type": "string",
            "description": "Plain language an agent can relay without re-deriving it."
          },
          "notice": {
            "type": "string",
            "description": "Informational-use notice. Always present."
          },
          "deleted": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              }
            },
            "required": [
              "id"
            ]
          }
        },
        "required": [
          "as_of",
          "summary",
          "notice",
          "deleted"
        ]
      },
      "CompareList": {
        "type": "object",
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601. How current the data in this response is."
          },
          "summary": {
            "type": "string",
            "description": "Plain language an agent can relay without re-deriving it."
          },
          "notice": {
            "type": "string",
            "description": "Informational-use notice. Always present."
          },
          "comparisons": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CompareSummary"
            },
            "description": "One page, newest first. Read one with `GET /v1/compare/{id}` for its parts."
          },
          "next_cursor": {
            "description": "Pass as `cursor` to get the next page. Null when this page is the last.",
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "as_of",
          "summary",
          "notice",
          "comparisons",
          "next_cursor"
        ]
      },
      "ComparePart": {
        "type": "object",
        "properties": {
          "mpn": {
            "type": "string"
          },
          "manufacturer": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "reference": {
            "type": "boolean",
            "description": "True for the part the others are compared with."
          },
          "match": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "direct",
                  "parametric_equivalent",
                  "similar",
                  "upgrade"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "For an alternate of the part you sent: the parts catalogue's classification. Null for the reference, for your own list, and when none is given."
          },
          "lifecycle": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "Active",
                  "Mature",
                  "NRND",
                  "EOL",
                  "Obsolete"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "Where the part is in its life. Null when no source states one."
          },
          "specs": {
            "type": "object",
            "propertyNames": {
              "type": "string"
            },
            "additionalProperties": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ]
            },
            "description": "One entry per name in `columns`: the value the part's datasheet states, or null when it states none."
          },
          "datasheet_url": {
            "anyOf": [
              {
                "type": "string",
                "format": "uri"
              },
              {
                "type": "null"
              }
            ],
            "description": "The datasheet the values were read from. Null when none was found."
          },
          "supply": {
            "type": "object",
            "properties": {
              "stock": {
                "description": "Units held across the distributors we can see.",
                "$ref": "#/components/schemas/Measure"
              },
              "distributor_count": {
                "anyOf": [
                  {
                    "type": "integer",
                    "minimum": 0
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Distributors holding stock. Null when supply could not be read; `stock` says why."
              },
              "lead_time_median": {
                "$ref": "#/components/schemas/Measure"
              }
            },
            "required": [
              "stock",
              "distributor_count",
              "lead_time_median"
            ]
          },
          "comparison": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "verdicts": {
                    "type": "object",
                    "propertyNames": {
                      "type": "string"
                    },
                    "additionalProperties": {
                      "type": "string",
                      "enum": [
                        "same",
                        "covers",
                        "narrower",
                        "higher",
                        "lower",
                        "different",
                        "not_stated"
                      ],
                      "description": "How this part's value stands against the reference part's. `same`: the same value, however written. `covers`: its range contains the reference's value or range. `narrower`: its range does not contain the reference's whole range. `higher` / `lower`: a single value above or below; whether that is better depends on the specification. `different`: the two cannot be put on one scale. `not_stated`: one of the datasheets states none."
                    },
                    "description": "One verdict per name in `columns`."
                  },
                  "stated": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Specifications both datasheets state."
                  },
                  "same_or_covers": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Of those, `same` or `covers`."
                  },
                  "summary": {
                    "type": "string"
                  }
                },
                "required": [
                  "verdicts",
                  "stated",
                  "same_or_covers",
                  "summary"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "Against the reference part. Null on the reference itself."
          }
        },
        "required": [
          "mpn",
          "manufacturer",
          "reference",
          "match",
          "lifecycle",
          "specs",
          "datasheet_url",
          "supply",
          "comparison"
        ]
      },
      "CompareRequest": {
        "type": "object",
        "properties": {
          "parts": {
            "description": "Your own list: 2 to 6 parts, compared as given. The first is the reference the others are compared with. Send this or `mpn`, not both.",
            "minItems": 2,
            "maxItems": 6,
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "mpn": {
                  "type": "string",
                  "minLength": 1,
                  "description": "The manufacturer part number."
                },
                "manufacturer": {
                  "description": "The maker. Only needed when several companies make a part with this number.",
                  "type": "string",
                  "minLength": 1
                }
              },
              "required": [
                "mpn"
              ]
            }
          },
          "mpn": {
            "description": "One part, compared with its alternates (the list `GET /v1/alternates` returns). If the part has no alternates, the comparison still completes, with this part alone and no `columns`. To compare parts you name with each other, send them all in `parts` instead. Send this or `parts`, not both.",
            "type": "string",
            "minLength": 1
          },
          "manufacturer": {
            "description": "The maker of `mpn`. Only needed when several companies make a part with this number.",
            "type": "string",
            "minLength": 1
          }
        }
      },
      "CompareSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "processing",
              "complete",
              "failed"
            ],
            "description": "`processing` until the comparison ends; `complete` with its parts; `failed` when it could not be completed (submit it again)."
          },
          "reference_mpn": {
            "type": "string",
            "description": "The part the others are compared with."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "completed_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ]
          },
          "parts": {
            "type": "integer",
            "minimum": 0,
            "description": "How many parts were compared, the reference included. 0 while processing."
          }
        },
        "required": [
          "id",
          "status",
          "reference_mpn",
          "created_at",
          "completed_at",
          "parts"
        ]
      },
      "CompareWebhookEvent": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "const": "compare.completed",
            "description": "A comparison you started with `POST /v1/compare` has ended."
          },
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "description": "When it ended. The same on every attempt."
          },
          "data": {
            "type": "object",
            "properties": {
              "compare_id": {
                "type": "string",
                "format": "uuid",
                "description": "The comparison: read its parts with `GET /v1/compare/{id}` when `status` is `complete`."
              },
              "status": {
                "type": "string",
                "enum": [
                  "complete",
                  "failed"
                ],
                "description": "`complete` with its parts, or `failed` (submit the comparison again)."
              },
              "end_customer_id": {
                "anyOf": [
                  {
                    "type": "string",
                    "format": "uuid"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "The customer the comparison belongs to, when it was started with a key made with `POST /v1/keys`; null otherwise."
              }
            },
            "required": [
              "compare_id",
              "status",
              "end_customer_id"
            ]
          }
        },
        "required": [
          "type",
          "timestamp",
          "data"
        ]
      },
      "CountryShare": {
        "type": "object",
        "properties": {
          "country": {
            "type": "string"
          },
          "country_code": {
            "description": "ISO 3166-1 alpha-2 code of `country`. Kosovo is `XK`, the code in common use for it.",
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "lines": {
            "type": "integer",
            "minimum": 0
          },
          "share_percent": {
            "type": "number",
            "description": "Share of ALL lines in the BOM, unresolved included, 0-100."
          },
          "worst_tier": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "low",
                  "medium",
                  "high",
                  "critical"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "The highest tier among this country's scored lines. Null when none of them is scored."
          }
        },
        "required": [
          "country",
          "country_code",
          "lines",
          "share_percent",
          "worst_tier"
        ]
      },
      "DataNote": {
        "type": "object",
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "manufacturer_not_listed",
              "part_not_listed",
              "part_number_identified",
              "part_number_needed"
            ],
            "description": "`manufacturer_not_listed`: the manufacturer given is not among the makers distributors list for this part number. `part_not_listed`: no distributor lists this part number. `part_number_identified`: the line was analyzed under a different part number than the one submitted. `part_number_needed`: what was submitted is not a manufacturer part number that can be identified, so the line is not scored; the `summary` says where to add one (the spreadsheet cell for an uploaded file)."
          },
          "summary": {
            "type": "string",
            "description": "One sentence describing what was found, to relay as written."
          },
          "suggestions": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "For `manufacturer_not_listed`, the makers distributors list for this part number, most listed first. For `part_not_listed`, real part numbers that begin with the one submitted. For `part_number_identified`, the part number the line was analyzed as. For `part_number_needed`, a part number the line may refer to, to be confirmed before use. May be empty."
          }
        },
        "required": [
          "kind",
          "summary",
          "suggestions"
        ]
      },
      "DisruptionEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "minimum": 0,
            "description": "The event id. The same id `/v1/parts` and `/v1/bom` return for this event."
          },
          "title": {
            "type": "string"
          },
          "summary": {
            "description": "What happened, in a few sentences.",
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "severity": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "low",
                  "medium",
                  "high",
                  "critical"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "How serious the event is in itself, not its effect on any one part."
          },
          "report_status": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "confirmed",
                  "developing",
                  "probable",
                  "rumor",
                  "risk"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "How firm the reporting is: `confirmed`, `developing`, `probable`, `rumor`, or `risk` (a warning about something that has not happened)."
          },
          "country": {
            "description": "Where it happens. Null when it spans several countries.",
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "country_code": {
            "description": "ISO 3166-1 alpha-2 code of `country`.",
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "region": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "Asia-Pacific",
                  "Europe",
                  "North America",
                  "Middle East",
                  "Latin America",
                  "Africa",
                  "Global"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "Regions as supply-chain news is grouped, not the UN regions on distributors; to match a distributor or a part's origin, filter by `country` (an ISO 3166-1 alpha-2 code, as `country_code` on distributors and `manufacturing_country_code` on parts and BOM lines)."
          },
          "categories": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "semiconductors",
                "electronics",
                "pcb",
                "connectors",
                "sensors",
                "motors",
                "drives",
                "power_supplies",
                "batteries",
                "cables_wire",
                "rf_microwave",
                "optics",
                "bearings",
                "mechanical",
                "hydraulics",
                "pneumatics",
                "seals_gaskets",
                "fluid_handling",
                "thermal",
                "industrial_automation",
                "filtration",
                "safety_equipment",
                "metals",
                "chemicals",
                "petrochemicals",
                "plastics",
                "raw_materials",
                "pharmaceuticals",
                "active_ingredients",
                "medical_devices",
                "agriculture",
                "fertilizers",
                "commodities",
                "automotive",
                "energy",
                "logistics"
              ]
            },
            "description": "Component categories the event affects."
          },
          "root_cause": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "natural_disaster",
                  "geopolitical_action",
                  "regulatory_change",
                  "labor_disruption",
                  "industrial_incident",
                  "infrastructure_failure",
                  "cyber_attack"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "What kind of cause lies behind the event."
          },
          "supply_chain_impact": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "shortage",
                "logistics_disruption",
                "production_halt",
                "capacity_reduction",
                "cost_increase",
                "demand_spike",
                "trade_restriction",
                "supplier_failure",
                "quality_issue",
                "allocation"
              ]
            },
            "description": "Kinds of effect on supply."
          },
          "sourcing_implication": {
            "description": "What it means for a buyer of the affected categories.",
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "recommended_action": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "monitor",
                  "diversify",
                  "qualify_alternate",
                  "reroute",
                  "urgent"
                ]
              },
              {
                "type": "null"
              }
            ]
          },
          "sourcing_urgency": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "low",
                  "medium",
                  "high",
                  "critical"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "How soon a buyer should act."
          },
          "regions_exposed": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "Asia-Pacific",
                "Europe",
                "North America",
                "Middle East",
                "Latin America",
                "Africa",
                "Global"
              ]
            },
            "description": "Regions whose supply is exposed to it. Regions as supply-chain news is grouped, not the UN regions on distributors; to match a distributor or a part's origin, filter by `country` (an ISO 3166-1 alpha-2 code, as `country_code` on distributors and `manufacturing_country_code` on parts and BOM lines)."
          },
          "lead_time": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/EventLeadTime"
              },
              {
                "type": "null"
              }
            ],
            "description": "Added lead time for affected parts. Null when none is available."
          },
          "shipping_check": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "change_from_normal_percent": {
                    "type": "integer",
                    "description": "Change in port calls in the event's country during the event's week, against their usual level for the same time of year, in percent. Negative is below usual, positive above."
                  },
                  "interpretation": {
                    "type": "string",
                    "description": "One line of plain language."
                  }
                },
                "required": [
                  "change_from_normal_percent",
                  "interpretation"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "Port activity in the event's country during the event's week, for events about ports or shipping. Null when there is none."
          },
          "source_urls": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            },
            "description": "News reports of this event."
          },
          "published_date": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "When the event was first reported."
          },
          "first_seen": {
            "type": "string",
            "format": "date-time",
            "description": "When PartFinder first recorded the event."
          }
        },
        "required": [
          "id",
          "title",
          "summary",
          "severity",
          "report_status",
          "country",
          "country_code",
          "region",
          "categories",
          "root_cause",
          "supply_chain_impact",
          "sourcing_implication",
          "recommended_action",
          "sourcing_urgency",
          "regions_exposed",
          "lead_time",
          "shipping_check",
          "source_urls",
          "published_date",
          "first_seen"
        ]
      },
      "Distributor": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "country": {
            "description": "The distributor's country. Null when it is not a single country.",
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "country_code": {
            "description": "ISO 3166-1 alpha-2 code of `country`. Kosovo is `XK`, the code in common use for it.",
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "regions": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "Africa",
                "Americas",
                "Asia",
                "Europe",
                "Oceania"
              ]
            },
            "description": "The regions this listing serves: the region of `country` when a country is known, otherwise the regions the distributor states for this storefront. Not where the part is made. Empty when no region is stated. These are the UN geographic regions, not the region values of `GET /v1/events`; to find events affecting a distributor, filter events by its `country_code`."
          },
          "subregion": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "Australia and New Zealand",
                  "Central Asia",
                  "Eastern Asia",
                  "Eastern Europe",
                  "Latin America and the Caribbean",
                  "Melanesia",
                  "Micronesia",
                  "Northern Africa",
                  "Northern America",
                  "Northern Europe",
                  "Polynesia",
                  "South-eastern Asia",
                  "Southern Asia",
                  "Southern Europe",
                  "Sub-Saharan Africa",
                  "Western Asia",
                  "Western Europe"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "The UN sub-region of `country` (for example Western Asia). Not where the part is made. Null when no country is known. These are UN geographic sub-regions, not the region values of `GET /v1/events`; to find events affecting a distributor, filter events by its `country_code`."
          },
          "stock": {
            "$ref": "#/components/schemas/Measure"
          }
        },
        "required": [
          "name",
          "country",
          "country_code",
          "regions",
          "subregion",
          "stock"
        ]
      },
      "DriverBand": {
        "type": "object",
        "properties": {
          "tier": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "low",
                  "medium",
                  "high",
                  "critical"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "The band this driver falls in. Null when there is no data for it; `interpretation` says why."
          },
          "interpretation": {
            "type": "string",
            "description": "One line of plain language about this driver for this part."
          }
        },
        "required": [
          "tier",
          "interpretation"
        ]
      },
      "EndCustomerKey": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The key id. Use it to revoke the key."
          },
          "name": {
            "type": "string",
            "description": "The key's name."
          },
          "end_customer_id": {
            "type": "string",
            "format": "uuid",
            "description": "The end customer the key belongs to. Every key of one end customer sees the same data."
          },
          "company": {
            "type": "string",
            "description": "The end customer's company name."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the key was created."
          }
        },
        "required": [
          "id",
          "name",
          "end_customer_id",
          "company",
          "created_at"
        ]
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "enum": [
              "authentication_required",
              "invalid_key",
              "invalid_request",
              "not_a_specific_part",
              "part_not_found",
              "verification_unavailable",
              "generation_failed",
              "bom_too_large",
              "bom_not_found",
              "bom_columns_not_found",
              "event_not_found",
              "rate_limited",
              "supplier_opted_out",
              "send_failed",
              "rfq_not_found",
              "rfq_bounced",
              "message_refused",
              "key_not_allowed",
              "insufficient_scope",
              "app_disconnected",
              "key_not_found",
              "key_name_taken",
              "webhook_endpoint_not_found",
              "not_a_parts_query",
              "search_not_found",
              "compare_not_found",
              "credits_exhausted",
              "payment_method_required",
              "payment_failed",
              "usage_limit_reached"
            ],
            "description": "`authentication_required` when no bearer token was sent. `invalid_key` when the key or sign-in is unknown, revoked or expired — deliberately indistinguishable — or the sign-in names no organization you belong to (the `summary` says what to do). `invalid_request` when a required parameter is missing. `not_a_specific_part` when the input does not identify one specific part. `part_not_found` when no supplier lists that part number exactly — usually a typo or a partial number. `verification_unavailable` when we could not check whether the part is real, and so declined to describe it; temporary, safe to retry. `generation_failed` when the report could not be produced, or a search or a comparison could not be started. `bom_too_large` when one call carries more lines than the limit; `reason` states both numbers. `bom_not_found` when no BOM with that id belongs to your organization (with a key made with `POST /v1/keys`, to that customer). `bom_columns_not_found` when an uploaded file has no column we can identify as part numbers; `reason` lists the headers seen. `event_not_found` when no active event has that id. `rate_limited` when your organization has used its calls for this minute or this day; wait the seconds in the `Retry-After` header, then retry. `supplier_opted_out` when the supplier asked not to receive requests for quote from you, or from anyone through PartFinder. `send_failed` when the email could not be sent; nothing was stored, safe to retry. `rfq_not_found` when no request for quote with that id belongs to your organization (with a key made with `POST /v1/keys`, to that customer). `rfq_bounced` when the supplier's address could not receive the request; send a new request for quote to a working address. `message_refused` when the text was not sent; `summary` says what to change before sending again. `key_not_allowed` when a key that belongs to one of your customers calls an endpoint only your organization's own keys may call, or a caller signed in through an AI app manages keys or webhooks. `insufficient_scope` when an AI app signed in without the permission the call needs (`partfinder:read` to read, which includes looking up parts, alternates and events and uses credits for those; `partfinder:write` to change anything); the `summary` names it, and connecting the app again with it allowed fixes it. `app_disconnected` when the AI app the caller signed in through was disconnected in the console; signing in again does not help, allowing the app again in the console does. `key_not_found` when no live end-customer key with that id belongs to your organization. `key_name_taken` when a key with that name already exists in your organization, revoked keys included. `webhook_endpoint_not_found` when no webhook endpoint with that id belongs to your organization (with a key made with `POST /v1/keys`, to that customer). `not_a_parts_query` when a search query is not about a part; send a part number or describe the part. `search_not_found` when no search with that id belongs to your organization (with a key made with `POST /v1/keys`, to that customer). `compare_not_found` when no comparison with that id belongs to your organization (with a key made with `POST /v1/keys`, to that customer). `credits_exhausted` when your plan's credits for this month are used up; this request was not run and nothing was charged. Upgrade the plan at `billing_url`, then retry. `payment_method_required` when your organization is on pay as you go with no payment method; this request was not run and nothing was charged. Add a payment method or choose a plan at `billing_url`, then retry. `payment_failed` when pay as you go is paused because the last payment failed; this request was not run and nothing was charged. Update the payment method at `billing_url`, then retry. `usage_limit_reached` when your organization, on pay as you go, has reached its limit for unpaid usage; this request was not run and nothing was charged. Calls resume once the current payment goes through; follow it at `billing_url`. With a key made with `POST /v1/keys`, these four carry no `billing_url`: the `summary` asks your customer to contact your organization."
          },
          "summary": {
            "description": "One sentence you can relay to a user as-is, the same as `summary` on a successful response. Present whenever there is something useful to say about what went wrong.",
            "type": "string"
          },
          "reason": {
            "description": "The specific detail behind the error.",
            "type": "string"
          },
          "billing_url": {
            "description": "On `credits_exhausted`, `payment_method_required`, `payment_failed` and `usage_limit_reached`: the PartFinder console Billing page, where your organization manages its plan and payment method. Absent for a key made with `POST /v1/keys`, and for an organization whose plan is set by its contract.",
            "type": "string",
            "format": "uri"
          },
          "did_you_mean": {
            "description": "Real part numbers to retry with. On `not_a_specific_part` they are parts of the kind you described; on `part_not_found` they are the listed part numbers your input is a prefix of, so `TPS5433` returns TPS54331, TPS54332 and so on. Absent when there is nothing to suggest. Every entry is a real part number, never a guess.",
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "error"
        ]
      },
      "EventCountries": {
        "type": "object",
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601. How current the data in this response is."
          },
          "summary": {
            "type": "string",
            "description": "Plain language an agent can relay without re-deriving it."
          },
          "notice": {
            "type": "string",
            "description": "Informational-use notice. Always present."
          },
          "countries": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "country": {
                  "type": "string"
                },
                "country_code": {
                  "type": "string",
                  "description": "ISO 3166-1 alpha-2."
                },
                "event_count": {
                  "type": "integer",
                  "minimum": 0,
                  "description": "Events in this country on the risk map now. The country can have more active events: list them with `GET /v1/events?country=`."
                },
                "worst_severity": {
                  "type": "string",
                  "enum": [
                    "low",
                    "medium",
                    "high",
                    "critical"
                  ],
                  "description": "The highest severity among them."
                },
                "top_event_id": {
                  "type": "integer",
                  "minimum": 0,
                  "description": "The most severe of those events; open it with `GET /v1/events/{id}`."
                }
              },
              "required": [
                "country",
                "country_code",
                "event_count",
                "worst_severity",
                "top_event_id"
              ]
            },
            "description": "Countries on the risk map now, most severe first. Not every country with an active event."
          }
        },
        "required": [
          "as_of",
          "summary",
          "notice",
          "countries"
        ]
      },
      "EventLeadTime": {
        "type": "object",
        "properties": {
          "text": {
            "type": "string",
            "description": "The lead time or delay, as a range in words, for example \"+2-4 weeks\" or \"as much as 12 days\"."
          },
          "source": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "sentence": {
                    "type": "string",
                    "description": "The sentence in the report that states the lead time."
                  },
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "The report."
                  }
                },
                "required": [
                  "sentence",
                  "url"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "The report that states `text`, as written there. Null when `text` is an estimate."
          },
          "interpretation": {
            "type": "string",
            "description": "One line of plain language: who states this lead time, or that it is an estimate."
          }
        },
        "required": [
          "text",
          "source",
          "interpretation"
        ]
      },
      "EventList": {
        "type": "object",
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601. How current the data in this response is."
          },
          "summary": {
            "type": "string",
            "description": "Plain language an agent can relay without re-deriving it."
          },
          "notice": {
            "type": "string",
            "description": "Informational-use notice. Always present."
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DisruptionEvent"
            },
            "description": "Newest first."
          },
          "next_cursor": {
            "description": "Pass as `cursor` for the next page. Null on the last page.",
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "as_of",
          "summary",
          "notice",
          "events",
          "next_cursor"
        ]
      },
      "EventResponse": {
        "type": "object",
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601. How current the data in this response is."
          },
          "summary": {
            "type": "string",
            "description": "Plain language an agent can relay without re-deriving it."
          },
          "notice": {
            "type": "string",
            "description": "Informational-use notice. Always present."
          },
          "event": {
            "$ref": "#/components/schemas/DisruptionEvent"
          }
        },
        "required": [
          "as_of",
          "summary",
          "notice",
          "event"
        ]
      },
      "GapTally": {
        "type": "object",
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "single_source",
              "no_supplier",
              "concentration_risk",
              "high_risk_region",
              "lifecycle_risk"
            ],
            "description": "`single_source`: the only manufacturer for its category in this BOM. `no_supplier`: no supplier named on the line; it counts in the gaps driver. `concentration_risk`: a large share of the BOM is made in one country. `high_risk_region`: made in a high-exposure country. `lifecycle_risk`: end of life, obsolete, or not recommended for new designs."
          },
          "lines": {
            "type": "integer",
            "minimum": 0
          },
          "interpretation": {
            "type": "string"
          }
        },
        "required": [
          "kind",
          "lines",
          "interpretation"
        ]
      },
      "KeyCreated": {
        "type": "object",
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601. How current the data in this response is."
          },
          "summary": {
            "type": "string",
            "description": "Plain language an agent can relay without re-deriving it."
          },
          "notice": {
            "type": "string",
            "description": "Informational-use notice. Always present."
          },
          "key": {
            "$ref": "#/components/schemas/EndCustomerKey"
          },
          "secret": {
            "type": "string",
            "description": "The key itself, shown only in this response: give it to your customer's integration now. It cannot be shown again."
          }
        },
        "required": [
          "as_of",
          "summary",
          "notice",
          "key",
          "secret"
        ]
      },
      "KeyList": {
        "type": "object",
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601. How current the data in this response is."
          },
          "summary": {
            "type": "string",
            "description": "Plain language an agent can relay without re-deriving it."
          },
          "notice": {
            "type": "string",
            "description": "Informational-use notice. Always present."
          },
          "keys": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EndCustomerKey"
            },
            "description": "Every end-customer key of your organization that is not revoked, newest first. Secrets are never listed."
          }
        },
        "required": [
          "as_of",
          "summary",
          "notice",
          "keys"
        ]
      },
      "KeyRequest": {
        "type": "object",
        "properties": {
          "company": {
            "description": "Your customer's company name. Required for a new end customer; it becomes the requester company of the requests for quote this key sends when they name none. Leave it out when `end_customer_id` is set.",
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "end_customer_id": {
            "description": "Add a key for an end customer you already created, for example to replace a key: create the new one, move your customer over to it, then revoke the old one. Its data stays with the end customer.",
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "description": "A name for the key, unique in your organization, including revoked keys. One is made up when absent.",
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          }
        }
      },
      "KeyRevoked": {
        "type": "object",
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601. How current the data in this response is."
          },
          "summary": {
            "type": "string",
            "description": "Plain language an agent can relay without re-deriving it."
          },
          "notice": {
            "type": "string",
            "description": "Informational-use notice. Always present."
          },
          "revoked": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              }
            },
            "required": [
              "id"
            ]
          }
        },
        "required": [
          "as_of",
          "summary",
          "notice",
          "revoked"
        ]
      },
      "Measure": {
        "type": "object",
        "properties": {
          "value": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ]
          },
          "unit": {
            "type": "string"
          },
          "reason": {
            "description": "Present only when value is null.",
            "type": "string"
          }
        },
        "required": [
          "value",
          "unit"
        ]
      },
      "NearbySupply": {
        "type": "object",
        "properties": {
          "country": {
            "type": "string",
            "description": "The buyer country sent in `country`, as a name."
          },
          "country_code": {
            "type": "string",
            "description": "ISO 3166-1 alpha-2 code of `country`. Kosovo is `XK`, the code in common use for it."
          },
          "region": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "Africa",
                  "Americas",
                  "Asia",
                  "Europe",
                  "Oceania"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "The region that country belongs to. Null when it has none. These are the UN geographic regions, not the region values of `GET /v1/events`; to find events affecting the buyer's country, filter events by that country's ISO 3166-1 alpha-2 code."
          },
          "summary": {
            "type": "string",
            "description": "One sentence on stock near the buyer, for relaying as-is."
          },
          "distributors": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "country": {
                  "description": "The distributor's country. Null when it is not a single country.",
                  "anyOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "country_code": {
                  "description": "ISO 3166-1 alpha-2 code of `country`. Kosovo is `XK`, the code in common use for it.",
                  "anyOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "regions": {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "enum": [
                      "Africa",
                      "Americas",
                      "Asia",
                      "Europe",
                      "Oceania"
                    ]
                  },
                  "description": "The regions this listing serves: the region of `country` when a country is known, otherwise the regions the distributor states for this storefront. Not where the part is made. Empty when no region is stated. These are the UN geographic regions, not the region values of `GET /v1/events`; to find events affecting a distributor, filter events by its `country_code`."
                },
                "subregion": {
                  "anyOf": [
                    {
                      "type": "string",
                      "enum": [
                        "Australia and New Zealand",
                        "Central Asia",
                        "Eastern Asia",
                        "Eastern Europe",
                        "Latin America and the Caribbean",
                        "Melanesia",
                        "Micronesia",
                        "Northern Africa",
                        "Northern America",
                        "Northern Europe",
                        "Polynesia",
                        "South-eastern Asia",
                        "Southern Asia",
                        "Southern Europe",
                        "Sub-Saharan Africa",
                        "Western Asia",
                        "Western Europe"
                      ]
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "The UN sub-region of `country` (for example Western Asia). Not where the part is made. Null when no country is known. These are UN geographic sub-regions, not the region values of `GET /v1/events`; to find events affecting a distributor, filter events by its `country_code`."
                },
                "stock": {
                  "$ref": "#/components/schemas/Measure"
                },
                "proximity": {
                  "type": "string",
                  "enum": [
                    "same_country",
                    "same_region"
                  ],
                  "description": "`same_country`: in the buyer's country. `same_region`: elsewhere in its region."
                }
              },
              "required": [
                "name",
                "country",
                "country_code",
                "regions",
                "subregion",
                "stock",
                "proximity"
              ]
            },
            "description": "Distributors holding stock in the buyer's country first, then elsewhere in its region, largest first within each. Empty when none do."
          }
        },
        "required": [
          "country",
          "country_code",
          "region",
          "summary",
          "distributors"
        ]
      },
      "PartReport": {
        "type": "object",
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601. How current the data in this response is."
          },
          "summary": {
            "type": "string",
            "description": "Plain language an agent can relay without re-deriving it."
          },
          "notice": {
            "type": "string",
            "description": "Informational-use notice. Always present."
          },
          "notes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DataNote"
            },
            "description": "Where supplier catalogues disagree with the `manufacturer` you passed for this part number. Empty when there is nothing to note."
          },
          "part": {
            "type": "object",
            "properties": {
              "mpn": {
                "type": "string",
                "description": "The part number you sent, normalized in format only: an all-lowercase part number is uppercased, and a `manufacturer` value repeated inside it is removed."
              },
              "manufacturer": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "category": {
                "description": "The specific type of part, e.g. \"DC-DC Buck Converter\". A BOM line gives the broad class instead, e.g. \"Semiconductors\".",
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "lifecycle": {
                "anyOf": [
                  {
                    "type": "string",
                    "enum": [
                      "Active",
                      "Mature",
                      "NRND",
                      "EOL",
                      "Obsolete"
                    ]
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Where the part is in its life. Null when no source states one."
              },
              "manufacturing_country": {
                "description": "Where the part is made, not where the company is headquartered. One country. When `manufacturer` is given, where that manufacturer makes the part, if known. Null when it could not be established.",
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "manufacturing_country_code": {
                "description": "ISO 3166-1 alpha-2 code of `manufacturing_country`. Kosovo is `XK`, the code in common use for it.",
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "mpn",
              "manufacturer",
              "category",
              "lifecycle",
              "manufacturing_country",
              "manufacturing_country_code"
            ]
          },
          "risk": {
            "type": "object",
            "properties": {
              "composite": {
                "description": "Combined from the four drivers below.",
                "$ref": "#/components/schemas/Score"
              },
              "tier": {
                "type": "string",
                "enum": [
                  "low",
                  "medium",
                  "high",
                  "critical"
                ],
                "description": "The band `composite` falls in."
              },
              "drivers": {
                "type": "object",
                "properties": {
                  "geography": {
                    "description": "Risk carried by the country the part is made in. `tier` is null when the origin could not be established; treat that as missing information, not as a level of risk.",
                    "$ref": "#/components/schemas/DriverBand"
                  },
                  "lead_time_volatility": {
                    "description": "How unstable the lead times suppliers are quoting now are. `tier` is null when no supplier quotes one.",
                    "$ref": "#/components/schemas/DriverBand"
                  },
                  "disruption_exposure": {
                    "description": "Exposure to current disruption events affecting this part; the events are in `disruption_events`.",
                    "$ref": "#/components/schemas/DriverBand"
                  },
                  "replacement_difficulty": {
                    "description": "How hard this part would be to substitute.",
                    "$ref": "#/components/schemas/DriverBand"
                  }
                },
                "required": [
                  "geography",
                  "lead_time_volatility",
                  "disruption_exposure",
                  "replacement_difficulty"
                ],
                "description": "What drives the score, each as a band and a sentence."
              },
              "why_at_risk": {
                "type": "string",
                "description": "The facts behind this part's risk, in plain sentences: where it is made, stock, quoted lead times, listed disruptions and alternates. Every figure in it is one this response carries."
              },
              "drivers_detail": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "title": {
                      "type": "string"
                    },
                    "description": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "title",
                    "description"
                  ]
                },
                "description": "One item per factor this response has figures for (lead time, alternates, disruptions): a title and a sentence stating those figures. Empty when none applies."
              }
            },
            "required": [
              "composite",
              "tier",
              "drivers",
              "why_at_risk",
              "drivers_detail"
            ],
            "description": "This part's risk on its own. A line in `GET /v1/bom/{id}` is scored within its BOM, with different drivers, so the same part can score differently on the two endpoints."
          },
          "supply": {
            "type": "object",
            "properties": {
              "stock": {
                "description": "Units held across the distributors we can see.",
                "$ref": "#/components/schemas/Measure"
              },
              "lead_time_median": {
                "$ref": "#/components/schemas/Measure"
              },
              "lead_time_min": {
                "description": "The 25th percentile of quoted lead times: the short end of the middle half of quotes. Null when no distributor quotes a lead time, or when the middle half of quotes is a single figure; `reason` says which.",
                "$ref": "#/components/schemas/Measure"
              },
              "lead_time_max": {
                "description": "The 75th percentile of quoted lead times: the long end of the middle half of quotes. Null when no distributor quotes a lead time, or when the middle half of quotes is a single figure; `reason` says which.",
                "$ref": "#/components/schemas/Measure"
              },
              "distributor_count": {
                "type": "integer",
                "minimum": 0,
                "description": "Distributors holding stock of this part. `distributors` shows only some of them."
              },
              "distributors": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Distributor"
                },
                "description": "The distributors holding the most stock, largest first. Not every distributor is listed: `distributor_count` is the total. Rows can share the same inventory, so do not add them up; `stock` is the total."
              },
              "nearby": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/NearbySupply"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Where to buy near the buyer, when the `country` parameter was sent. Null otherwise. The risk score does not depend on it."
              }
            },
            "required": [
              "stock",
              "lead_time_median",
              "lead_time_min",
              "lead_time_max",
              "distributor_count",
              "distributors",
              "nearby"
            ]
          },
          "datasheet": {
            "type": "object",
            "properties": {
              "url": {
                "anyOf": [
                  {
                    "type": "string",
                    "format": "uri"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "source": {
                "anyOf": [
                  {
                    "type": "string",
                    "enum": [
                      "distributor",
                      "other"
                    ]
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "`distributor` when the link came from a distributor listing, `other` otherwise. The document may be hosted by a distributor rather than the manufacturer."
              }
            },
            "required": [
              "url",
              "source"
            ]
          },
          "disruption_events": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "kind": {
                  "type": "string",
                  "enum": [
                    "news",
                    "port_activity",
                    "sea_route"
                  ],
                  "description": "`news`: a reported disruption. `port_activity`: ship traffic at the ports of a country this part is made in or sold from is well under its usual level. `sea_route`: the same for a sea passage this part's trade uses."
                },
                "id": {
                  "anyOf": [
                    {
                      "type": "integer",
                      "minimum": 0
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "The event id for `news`; null otherwise."
                },
                "title": {
                  "type": "string"
                },
                "root_cause": {
                  "anyOf": [
                    {
                      "type": "string",
                      "enum": [
                        "natural_disaster",
                        "geopolitical_action",
                        "regulatory_change",
                        "labor_disruption",
                        "industrial_incident",
                        "infrastructure_failure",
                        "cyber_attack"
                      ]
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "What kind of cause lies behind a `news` event, as on `GET /v1/events`. Null otherwise."
                },
                "country": {
                  "anyOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "severity": {
                  "anyOf": [
                    {
                      "type": "string",
                      "enum": [
                        "low",
                        "medium",
                        "high",
                        "critical"
                      ]
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "For `news`: how serious the event is in itself, not its effect on this part. Null for `port_activity` and `sea_route`, whose `title` states the slowdown."
                },
                "published_date": {
                  "anyOf": [
                    {
                      "type": "string",
                      "format": "date-time"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "For `news`, when it was reported; otherwise the last day of the week it covers."
                },
                "url": {
                  "anyOf": [
                    {
                      "type": "string",
                      "format": "uri"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "A source article for `news`; may be null. Always null otherwise."
                }
              },
              "required": [
                "kind",
                "id",
                "title",
                "root_cause",
                "country",
                "severity",
                "published_date",
                "url"
              ]
            },
            "description": "The reported events and current shipping conditions that most affect this part's `disruption_exposure`, strongest first: ordered by effect on this part's score, not by `severity`. Empty when none does."
          },
          "alternates": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Alternate"
            },
            "description": "Parts that could replace this one, up to five: the same list as `GET /v1/alternates`. Never this part itself or the same part in another package or ordering variant. Not ranked. Empty when none was found."
          },
          "recommended_actions": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "What a procurement team should do next."
          }
        },
        "required": [
          "as_of",
          "summary",
          "notice",
          "notes",
          "part",
          "risk",
          "supply",
          "datasheet",
          "disruption_events",
          "alternates",
          "recommended_actions"
        ]
      },
      "Pong": {
        "type": "object",
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601. How current the data in this response is."
          },
          "summary": {
            "type": "string",
            "description": "Plain language an agent can relay without re-deriving it."
          },
          "notice": {
            "type": "string",
            "description": "Informational-use notice. Always present."
          },
          "authenticated": {
            "type": "object",
            "properties": {
              "organization_id": {
                "type": "string"
              },
              "key_id": {
                "type": "string"
              }
            },
            "required": [
              "organization_id",
              "key_id"
            ]
          }
        },
        "required": [
          "as_of",
          "summary",
          "notice",
          "authenticated"
        ]
      },
      "Rfq": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "sent",
              "replied",
              "bounced"
            ],
            "description": "`sent` until the supplier answers, `replied` once they have, `bounced` when the address could not receive mail."
          },
          "supplier": {
            "type": "object",
            "properties": {
              "email": {
                "type": "string"
              },
              "name": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "contact_name": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "email",
              "name",
              "contact_name"
            ]
          },
          "requester": {
            "type": "object",
            "properties": {
              "company": {
                "type": "string"
              },
              "contact_name": {
                "type": "string"
              },
              "title": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "company",
              "contact_name",
              "title"
            ]
          },
          "parts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RfqPart"
            }
          },
          "needed_by": {
            "anyOf": [
              {
                "type": "string",
                "format": "date"
              },
              {
                "type": "null"
              }
            ],
            "description": "The date the parts are needed by."
          },
          "notify_email": {
            "description": "Who is emailed when the supplier replies; null when none was given.",
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "first_reply_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ]
          },
          "follow_up_count": {
            "type": "integer",
            "minimum": 0,
            "description": "Messages sent after the first."
          },
          "replies_received": {
            "type": "integer",
            "minimum": 0,
            "description": "Replies received from the supplier."
          },
          "quotes_received": {
            "type": "integer",
            "minimum": 0,
            "description": "Of those replies, the ones that give a quote."
          },
          "messages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RfqMessage"
            },
            "description": "The thread, oldest first."
          }
        },
        "required": [
          "id",
          "status",
          "supplier",
          "requester",
          "parts",
          "needed_by",
          "notify_email",
          "created_at",
          "first_reply_at",
          "follow_up_count",
          "replies_received",
          "quotes_received",
          "messages"
        ]
      },
      "RfqDeleted": {
        "type": "object",
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601. How current the data in this response is."
          },
          "summary": {
            "type": "string",
            "description": "Plain language an agent can relay without re-deriving it."
          },
          "notice": {
            "type": "string",
            "description": "Informational-use notice. Always present."
          },
          "deleted": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              }
            },
            "required": [
              "id"
            ]
          }
        },
        "required": [
          "as_of",
          "summary",
          "notice",
          "deleted"
        ]
      },
      "RfqList": {
        "type": "object",
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601. How current the data in this response is."
          },
          "summary": {
            "type": "string",
            "description": "Plain language an agent can relay without re-deriving it."
          },
          "notice": {
            "type": "string",
            "description": "Informational-use notice. Always present."
          },
          "rfqs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RfqSummary"
            },
            "description": "One page, newest first. Read one with `GET /v1/rfq/{id}` for its messages."
          },
          "next_cursor": {
            "description": "Pass as `cursor` to get the next page. Null when this page is the last.",
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "as_of",
          "summary",
          "notice",
          "rfqs",
          "next_cursor"
        ]
      },
      "RfqMessage": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "direction": {
            "type": "string",
            "enum": [
              "outbound",
              "inbound"
            ],
            "description": "`outbound` from the requester, `inbound` from the supplier."
          },
          "subject": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "body": {
            "description": "The text as sent, or what the supplier wrote in this reply.",
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "sent_at": {
            "type": "string",
            "format": "date-time"
          },
          "attachments": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "filename": {
                  "type": "string"
                },
                "size": {
                  "type": "integer",
                  "minimum": 0,
                  "description": "Bytes."
                }
              },
              "required": [
                "filename",
                "size"
              ]
            },
            "description": "The names and sizes of files the message carried."
          },
          "reply_kind": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "quote",
                  "question",
                  "decline",
                  "other"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "On a supplier reply: `quote` when it gives a price or lead time, `question` when the supplier asks something before quoting, `decline` when they will not quote, `other` for anything else. Null on messages sent by the requester, or while it is not yet available."
          },
          "quote": {
            "anyOf": [
              {
                "description": "The quote stated in this reply. A field the reply does not state is null, with `reason`; `words` holds the supplier's own words.",
                "$ref": "#/components/schemas/RfqQuote"
              },
              {
                "type": "null"
              }
            ],
            "description": "On a supplier reply: the quote it states. Null on messages sent by the requester, or while it is not yet available."
          }
        },
        "required": [
          "id",
          "direction",
          "subject",
          "body",
          "sent_at",
          "attachments",
          "reply_kind",
          "quote"
        ]
      },
      "RfqMessageRequest": {
        "type": "object",
        "properties": {
          "body": {
            "type": "string",
            "minLength": 1,
            "description": "The text to send, as written. Sent under the requester's name in the same email thread."
          },
          "subject": {
            "description": "Omit it to reply under the thread's subject (\"Re: …\"), which keeps the conversation together.",
            "anyOf": [
              {
                "type": "string",
                "minLength": 1
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "body"
        ]
      },
      "RfqPart": {
        "type": "object",
        "properties": {
          "mpn": {
            "type": "string",
            "minLength": 1,
            "description": "The manufacturer part number."
          },
          "manufacturer": {
            "description": "The manufacturer, when you know it.",
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "quantity": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "description": "Units you want quoted."
          }
        },
        "required": [
          "mpn",
          "quantity"
        ]
      },
      "RfqQuote": {
        "type": "object",
        "properties": {
          "unit_price": {
            "type": "object",
            "properties": {
              "value": {
                "anyOf": [
                  {
                    "type": "number",
                    "minimum": 0
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Price per unit."
              },
              "words": {
                "description": "The supplier's exact words for this field. Null when the reply does not state it.",
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "reason": {
                "description": "Why `value` is null; null when it is filled.",
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "value",
              "words",
              "reason"
            ]
          },
          "currency": {
            "type": "object",
            "properties": {
              "value": {
                "description": "ISO 4217 code.",
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "words": {
                "description": "The supplier's exact words for this field. Null when the reply does not state it.",
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "reason": {
                "description": "Why `value` is null; null when it is filled.",
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "value",
              "words",
              "reason"
            ]
          },
          "quantity": {
            "type": "object",
            "properties": {
              "value": {
                "anyOf": [
                  {
                    "type": "integer",
                    "minimum": 0
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Units the price is for."
              },
              "words": {
                "description": "The supplier's exact words for this field. Null when the reply does not state it.",
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "reason": {
                "description": "Why `value` is null; null when it is filled.",
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "value",
              "words",
              "reason"
            ]
          },
          "lead_time_days": {
            "type": "object",
            "properties": {
              "value": {
                "anyOf": [
                  {
                    "type": "integer",
                    "minimum": 0
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Lead time in days."
              },
              "words": {
                "description": "The supplier's exact words for this field. Null when the reply does not state it.",
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "reason": {
                "description": "Why `value` is null; null when it is filled.",
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "value",
              "words",
              "reason"
            ]
          },
          "moq": {
            "type": "object",
            "properties": {
              "value": {
                "anyOf": [
                  {
                    "type": "integer",
                    "minimum": 0
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Minimum order quantity."
              },
              "words": {
                "description": "The supplier's exact words for this field. Null when the reply does not state it.",
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "reason": {
                "description": "Why `value` is null; null when it is filled.",
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "value",
              "words",
              "reason"
            ]
          }
        },
        "required": [
          "unit_price",
          "currency",
          "quantity",
          "lead_time_days",
          "moq"
        ]
      },
      "RfqRequest": {
        "type": "object",
        "properties": {
          "supplier": {
            "type": "object",
            "properties": {
              "email": {
                "type": "string",
                "format": "email",
                "description": "Where the request is sent: a real email goes to this address. Use only an address your user gave; never guess one."
              },
              "name": {
                "description": "The supplier company.",
                "anyOf": [
                  {
                    "type": "string",
                    "minLength": 1
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "contact_name": {
                "description": "The person you are writing to at the supplier, when you know them.",
                "anyOf": [
                  {
                    "type": "string",
                    "minLength": 1
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "email"
            ]
          },
          "requester": {
            "type": "object",
            "properties": {
              "company": {
                "description": "The company asking for the quote. When omitted: with a key made with `POST /v1/keys`, that customer's company; otherwise your organization's name.",
                "anyOf": [
                  {
                    "type": "string",
                    "minLength": 1
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "contact_name": {
                "type": "string",
                "minLength": 1,
                "description": "The person asking. The email is sent under \"<contact_name>, <company>\"."
              },
              "title": {
                "description": "Their role, for example \"Procurement Manager\", used when they introduce themselves.",
                "anyOf": [
                  {
                    "type": "string",
                    "minLength": 1
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "contact_name"
            ],
            "description": "Who the request is from."
          },
          "parts": {
            "minItems": 1,
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RfqPart"
            },
            "description": "The parts and quantities to quote."
          },
          "needed_by": {
            "description": "The date you need the parts delivered by, YYYY-MM-DD. Stated in the email so the supplier can say whether they can meet it.",
            "anyOf": [
              {
                "type": "string",
                "format": "date"
              },
              {
                "type": "null"
              }
            ]
          },
          "message": {
            "description": "Email text your user wrote, sent as written. Omit it unless your user gave the text; an email is then written for you.",
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "subject": {
                    "description": "Without a subject, one is written for you.",
                    "anyOf": [
                      {
                        "type": "string",
                        "minLength": 1
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "body": {
                    "type": "string",
                    "minLength": 1
                  }
                },
                "required": [
                  "body"
                ]
              },
              {
                "type": "null"
              }
            ]
          },
          "notify_email": {
            "description": "Who is emailed when the supplier replies. Required with a key you created for one of your customers (`POST /v1/keys`): only this address is told, never your organization. Optional with your organization's own keys.",
            "anyOf": [
              {
                "type": "string",
                "format": "email"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "supplier",
          "requester",
          "parts"
        ]
      },
      "RfqResponse": {
        "type": "object",
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601. How current the data in this response is."
          },
          "summary": {
            "type": "string",
            "description": "Plain language an agent can relay without re-deriving it."
          },
          "notice": {
            "type": "string",
            "description": "Informational-use notice. Always present."
          },
          "rfq": {
            "$ref": "#/components/schemas/Rfq"
          }
        },
        "required": [
          "as_of",
          "summary",
          "notice",
          "rfq"
        ]
      },
      "RfqSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "sent",
              "replied",
              "bounced"
            ],
            "description": "`sent` until the supplier answers, `replied` once they have, `bounced` when the address could not receive mail."
          },
          "supplier": {
            "type": "object",
            "properties": {
              "email": {
                "type": "string"
              },
              "name": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "contact_name": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "email",
              "name",
              "contact_name"
            ]
          },
          "requester": {
            "type": "object",
            "properties": {
              "company": {
                "type": "string"
              },
              "contact_name": {
                "type": "string"
              },
              "title": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "company",
              "contact_name",
              "title"
            ]
          },
          "parts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RfqPart"
            }
          },
          "needed_by": {
            "anyOf": [
              {
                "type": "string",
                "format": "date"
              },
              {
                "type": "null"
              }
            ],
            "description": "The date the parts are needed by."
          },
          "notify_email": {
            "description": "Who is emailed when the supplier replies; null when none was given.",
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "first_reply_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ]
          },
          "follow_up_count": {
            "type": "integer",
            "minimum": 0,
            "description": "Messages sent after the first."
          },
          "replies_received": {
            "type": "integer",
            "minimum": 0,
            "description": "Replies received from the supplier."
          },
          "quotes_received": {
            "type": "integer",
            "minimum": 0,
            "description": "Of those replies, the ones that give a quote."
          }
        },
        "required": [
          "id",
          "status",
          "supplier",
          "requester",
          "parts",
          "needed_by",
          "notify_email",
          "created_at",
          "first_reply_at",
          "follow_up_count",
          "replies_received",
          "quotes_received"
        ]
      },
      "Score": {
        "type": "object",
        "properties": {
          "value": {
            "type": "number"
          },
          "scale": {
            "type": "number",
            "description": "The maximum, e.g. 100 for a 0-100 score."
          },
          "direction": {
            "type": "string",
            "enum": [
              "higher_is_worse",
              "higher_is_better"
            ]
          },
          "interpretation": {
            "type": "string",
            "description": "One line of plain language: what this number means."
          }
        },
        "required": [
          "value",
          "scale",
          "direction",
          "interpretation"
        ]
      },
      "Search": {
        "type": "object",
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601. How current the data in this response is."
          },
          "summary": {
            "type": "string",
            "description": "Plain language an agent can relay without re-deriving it."
          },
          "notice": {
            "type": "string",
            "description": "Informational-use notice. Always present."
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "processing",
              "complete",
              "failed"
            ],
            "description": "`processing` until the search ends; `complete` with its rows; `failed` when it could not be completed (submit it again)."
          },
          "query": {
            "type": "string",
            "description": "The query you sent."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "completed_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "When the search ended. Null while processing."
          },
          "columns": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The specification names every row's `specs` uses, chosen for what was searched. Empty until complete."
          },
          "rows": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SearchRow"
            },
            "description": "The parts found. Empty until complete, and when nothing matched."
          },
          "insights": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "key_specs": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "The specifications to compare across these parts."
                  },
                  "applications": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Where parts like these are used."
                  }
                },
                "required": [
                  "key_specs",
                  "applications"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "A short reading of the results. Null while processing, or when there is none."
          }
        },
        "required": [
          "as_of",
          "summary",
          "notice",
          "id",
          "status",
          "query",
          "created_at",
          "completed_at",
          "columns",
          "rows",
          "insights"
        ]
      },
      "SearchAccepted": {
        "type": "object",
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601. How current the data in this response is."
          },
          "summary": {
            "type": "string",
            "description": "Plain language an agent can relay without re-deriving it."
          },
          "notice": {
            "type": "string",
            "description": "Informational-use notice. Always present."
          },
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Poll `GET /v1/search/{id}` with it."
          },
          "status": {
            "type": "string",
            "enum": [
              "processing",
              "complete",
              "failed"
            ],
            "description": "`processing` for a new search. A retry with an `Idempotency-Key` already used returns the search that key started, with its current status."
          }
        },
        "required": [
          "as_of",
          "summary",
          "notice",
          "id",
          "status"
        ]
      },
      "SearchDeleted": {
        "type": "object",
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601. How current the data in this response is."
          },
          "summary": {
            "type": "string",
            "description": "Plain language an agent can relay without re-deriving it."
          },
          "notice": {
            "type": "string",
            "description": "Informational-use notice. Always present."
          },
          "deleted": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              }
            },
            "required": [
              "id"
            ]
          }
        },
        "required": [
          "as_of",
          "summary",
          "notice",
          "deleted"
        ]
      },
      "SearchList": {
        "type": "object",
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601. How current the data in this response is."
          },
          "summary": {
            "type": "string",
            "description": "Plain language an agent can relay without re-deriving it."
          },
          "notice": {
            "type": "string",
            "description": "Informational-use notice. Always present."
          },
          "searches": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SearchSummary"
            },
            "description": "One page, newest first. Read one with `GET /v1/search/{id}` for its rows."
          },
          "next_cursor": {
            "description": "Pass as `cursor` to get the next page. Null when this page is the last.",
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "as_of",
          "summary",
          "notice",
          "searches",
          "next_cursor"
        ]
      },
      "SearchRequest": {
        "type": "object",
        "properties": {
          "query": {
            "type": "string",
            "minLength": 1,
            "description": "What to search for: a part number, or a description of the part (\"3A buck converter 28V\", \"6205 bearing\")."
          }
        },
        "required": [
          "query"
        ]
      },
      "SearchRow": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "The part number, or the product name when the source names no single part number."
          },
          "manufacturer": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "product_url": {
            "anyOf": [
              {
                "type": "string",
                "format": "uri"
              },
              {
                "type": "null"
              }
            ],
            "description": "The manufacturer's page for the part. Null when none was found."
          },
          "datasheet_url": {
            "anyOf": [
              {
                "type": "string",
                "format": "uri"
              },
              {
                "type": "null"
              }
            ],
            "description": "The part's datasheet. Null when none was found."
          },
          "lifecycle": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "Active",
                  "Mature",
                  "NRND",
                  "EOL",
                  "Obsolete"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "Where the part is in its life. Null when no source states one."
          },
          "specs": {
            "type": "object",
            "propertyNames": {
              "type": "string"
            },
            "additionalProperties": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ]
            },
            "description": "One entry per name in `columns`: the value the datasheet states, or null when it states none."
          },
          "supply": {
            "type": "object",
            "properties": {
              "stock": {
                "description": "Units held across the distributors we can see.",
                "$ref": "#/components/schemas/Measure"
              },
              "distributor_count": {
                "anyOf": [
                  {
                    "type": "integer",
                    "minimum": 0
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Distributors holding stock. Null when supply could not be read; `stock` says why."
              },
              "lead_time_median": {
                "$ref": "#/components/schemas/Measure"
              }
            },
            "required": [
              "stock",
              "distributor_count",
              "lead_time_median"
            ]
          }
        },
        "required": [
          "name",
          "manufacturer",
          "product_url",
          "datasheet_url",
          "lifecycle",
          "specs",
          "supply"
        ]
      },
      "SearchSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "processing",
              "complete",
              "failed"
            ],
            "description": "`processing` until the search ends; `complete` with its rows; `failed` when it could not be completed (submit it again)."
          },
          "query": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "completed_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ]
          },
          "rows": {
            "type": "integer",
            "minimum": 0,
            "description": "How many parts the search found. 0 while processing."
          }
        },
        "required": [
          "id",
          "status",
          "query",
          "created_at",
          "completed_at",
          "rows"
        ]
      },
      "SearchWebhookEvent": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "const": "search.completed",
            "description": "A search you started with `POST /v1/search` has ended."
          },
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "description": "When it ended. The same on every attempt."
          },
          "data": {
            "type": "object",
            "properties": {
              "search_id": {
                "type": "string",
                "format": "uuid",
                "description": "The search: read its rows with `GET /v1/search/{id}` when `status` is `complete`."
              },
              "status": {
                "type": "string",
                "enum": [
                  "complete",
                  "failed"
                ],
                "description": "`complete` with its rows, or `failed` (submit the search again)."
              },
              "end_customer_id": {
                "anyOf": [
                  {
                    "type": "string",
                    "format": "uuid"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "The customer the search belongs to, when it was started with a key made with `POST /v1/keys`; null otherwise."
              }
            },
            "required": [
              "search_id",
              "status",
              "end_customer_id"
            ]
          }
        },
        "required": [
          "type",
          "timestamp",
          "data"
        ]
      },
      "ShippingConditions": {
        "type": "object",
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601. How current the data in this response is."
          },
          "summary": {
            "type": "string",
            "description": "Plain language an agent can relay without re-deriving it."
          },
          "notice": {
            "type": "string",
            "description": "Informational-use notice. Always present."
          },
          "countries": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "change_from_normal_percent": {
                  "type": "integer",
                  "description": "Change in activity over the last 7 days against this country's or route's usual level for the same time of year, in percent. Negative is below usual, positive above."
                },
                "through": {
                  "type": "string",
                  "format": "date",
                  "description": "The last day the reading covers."
                },
                "interpretation": {
                  "type": "string",
                  "description": "One line of plain language."
                },
                "country": {
                  "type": "string"
                },
                "country_code": {
                  "type": "string",
                  "description": "ISO 3166-1 alpha-2."
                }
              },
              "required": [
                "change_from_normal_percent",
                "through",
                "interpretation",
                "country",
                "country_code"
              ]
            },
            "description": "Port calls per country, furthest below usual first. Countries with enough port traffic to read reliably."
          },
          "sea_routes": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "change_from_normal_percent": {
                  "type": "integer",
                  "description": "Change in activity over the last 7 days against this country's or route's usual level for the same time of year, in percent. Negative is below usual, positive above."
                },
                "through": {
                  "type": "string",
                  "format": "date",
                  "description": "The last day the reading covers."
                },
                "interpretation": {
                  "type": "string",
                  "description": "One line of plain language."
                },
                "name": {
                  "type": "string",
                  "description": "The strait, canal or sea route."
                }
              },
              "required": [
                "change_from_normal_percent",
                "through",
                "interpretation",
                "name"
              ]
            },
            "description": "Ship transits per sea route, furthest below usual first."
          }
        },
        "required": [
          "as_of",
          "summary",
          "notice",
          "countries",
          "sea_routes"
        ]
      },
      "WebhookEndpoint": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The endpoint id."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Where events are sent."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the endpoint was registered."
          }
        },
        "required": [
          "id",
          "url",
          "created_at"
        ]
      },
      "WebhookEndpointCreated": {
        "type": "object",
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601. How current the data in this response is."
          },
          "summary": {
            "type": "string",
            "description": "Plain language an agent can relay without re-deriving it."
          },
          "notice": {
            "type": "string",
            "description": "Informational-use notice. Always present."
          },
          "endpoint": {
            "$ref": "#/components/schemas/WebhookEndpoint"
          },
          "secret": {
            "type": "string",
            "description": "The signing secret for this endpoint, shown only in this response: store it now. Use it to check the `webhook-signature` header of every event we send, as the Standard Webhooks libraries do. It signs our messages and gives no access to the API."
          }
        },
        "required": [
          "as_of",
          "summary",
          "notice",
          "endpoint",
          "secret"
        ]
      },
      "WebhookEndpointDeleted": {
        "type": "object",
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601. How current the data in this response is."
          },
          "summary": {
            "type": "string",
            "description": "Plain language an agent can relay without re-deriving it."
          },
          "notice": {
            "type": "string",
            "description": "Informational-use notice. Always present."
          },
          "deleted": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              }
            },
            "required": [
              "id"
            ]
          }
        },
        "required": [
          "as_of",
          "summary",
          "notice",
          "deleted"
        ]
      },
      "WebhookEndpointList": {
        "type": "object",
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601. How current the data in this response is."
          },
          "summary": {
            "type": "string",
            "description": "Plain language an agent can relay without re-deriving it."
          },
          "notice": {
            "type": "string",
            "description": "Informational-use notice. Always present."
          },
          "endpoints": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEndpoint"
            },
            "description": "Every endpoint your organization registered, newest first; with a key made with `POST /v1/keys`, only that customer's. Secrets are never listed."
          }
        },
        "required": [
          "as_of",
          "summary",
          "notice",
          "endpoints"
        ]
      },
      "WebhookEndpointRequest": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "The https:// address on your server that receives the events. It must be on the public internet."
          }
        },
        "required": [
          "url"
        ]
      },
      "WebhookEvent": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "rfq.reply_received",
              "rfq.bounced"
            ],
            "description": "`rfq.reply_received` when a supplier replied to one of your requests for quote: read it with `GET /v1/rfq/{id}`. `rfq.bounced` when a request for quote could not be delivered to the supplier's address."
          },
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "description": "When it happened. The same on every attempt."
          },
          "data": {
            "type": "object",
            "properties": {
              "rfq_id": {
                "type": "string",
                "format": "uuid",
                "description": "The request for quote."
              },
              "message_id": {
                "anyOf": [
                  {
                    "type": "string",
                    "format": "uuid"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "The supplier's reply for `rfq.reply_received`; the message that bounced for `rfq.bounced`."
              },
              "end_customer_id": {
                "anyOf": [
                  {
                    "type": "string",
                    "format": "uuid"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "The customer the request for quote belongs to, when it was sent with a key made with `POST /v1/keys`; null otherwise."
              }
            },
            "required": [
              "rfq_id",
              "message_id",
              "end_customer_id"
            ]
          }
        },
        "required": [
          "type",
          "timestamp",
          "data"
        ]
      }
    }
  },
  "webhooks": {
    "rfq.reply_received": {
      "post": {
        "summary": "A supplier replied",
        "description": "Sent when a supplier replies to one of your requests for quote. Read the reply, and its quote when it is one, with `GET /v1/rfq/{id}`.",
        "tags": [
          "Webhooks"
        ],
        "security": [],
        "parameters": [
          {
            "name": "webhook-id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The event id, the same on every attempt: use it to ignore a repeat."
          },
          {
            "name": "webhook-timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "When this attempt was sent, in seconds since 1970-01-01 UTC. Reject an old one."
          },
          {
            "name": "webhook-signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The Standard Webhooks signature of this attempt, made with your endpoint's secret. Check it before trusting the body."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEvent"
              },
              "example": {
                "type": "rfq.reply_received",
                "timestamp": "2026-10-06T14:27:40.551Z",
                "data": {
                  "rfq_id": "5b0f3c1e-8a2d-4f6b-9c7e-1d2a3b4c5d6e",
                  "message_id": "2c8b6e1f-7d3a-4e9b-a1c5-6f0d2e4b8a73",
                  "end_customer_id": null
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Answer within 15 seconds; any other answer, or none, is retried over about three days."
          }
        }
      }
    },
    "rfq.bounced": {
      "post": {
        "summary": "A request for quote bounced",
        "description": "Sent when a request for quote, or a message in its thread, could not be delivered to the supplier's address. Send a new request for quote to a working address.",
        "tags": [
          "Webhooks"
        ],
        "security": [],
        "parameters": [
          {
            "name": "webhook-id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The event id, the same on every attempt: use it to ignore a repeat."
          },
          {
            "name": "webhook-timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "When this attempt was sent, in seconds since 1970-01-01 UTC. Reject an old one."
          },
          {
            "name": "webhook-signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The Standard Webhooks signature of this attempt, made with your endpoint's secret. Check it before trusting the body."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEvent"
              },
              "example": {
                "type": "rfq.bounced",
                "timestamp": "2026-10-06T08:41:40.000Z",
                "data": {
                  "rfq_id": "7c1e9a4b-2d3f-4e5a-8b6c-9d0e1f2a3b4c",
                  "message_id": "d4a7c2e9-1b5f-4c83-a6e0-9f2b7d1c3e58",
                  "end_customer_id": null
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Answer within 15 seconds; any other answer, or none, is retried over about three days."
          }
        }
      }
    },
    "search.completed": {
      "post": {
        "summary": "A search ended",
        "description": "Sent when a search you started with `POST /v1/search` has ended. Read its rows with `GET /v1/search/{id}` when `data.status` is `complete`; when it is `failed`, submit the search again.",
        "tags": [
          "Webhooks"
        ],
        "security": [],
        "parameters": [
          {
            "name": "webhook-id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The event id, the same on every attempt: use it to ignore a repeat."
          },
          {
            "name": "webhook-timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "When this attempt was sent, in seconds since 1970-01-01 UTC. Reject an old one."
          },
          {
            "name": "webhook-signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The Standard Webhooks signature of this attempt, made with your endpoint's secret. Check it before trusting the body."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SearchWebhookEvent"
              },
              "example": {
                "type": "search.completed",
                "timestamp": "2026-10-10T13:20:39.606Z",
                "data": {
                  "search_id": "e8413914-cea8-46b0-9c6b-8207a4157fc0",
                  "status": "complete",
                  "end_customer_id": null
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Answer within 15 seconds; any other answer, or none, is retried over about three days."
          }
        }
      }
    },
    "compare.completed": {
      "post": {
        "summary": "A comparison ended",
        "description": "Sent when a comparison you started with `POST /v1/compare` has ended. Read its parts with `GET /v1/compare/{id}` when `data.status` is `complete`; when it is `failed`, submit the comparison again.",
        "tags": [
          "Webhooks"
        ],
        "security": [],
        "parameters": [
          {
            "name": "webhook-id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The event id, the same on every attempt: use it to ignore a repeat."
          },
          {
            "name": "webhook-timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "When this attempt was sent, in seconds since 1970-01-01 UTC. Reject an old one."
          },
          {
            "name": "webhook-signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The Standard Webhooks signature of this attempt, made with your endpoint's secret. Check it before trusting the body."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CompareWebhookEvent"
              },
              "example": {
                "type": "compare.completed",
                "timestamp": "2026-10-12T02:49:29.479Z",
                "data": {
                  "compare_id": "f2d22c25-e694-428b-bbb8-3948e60d3c7b",
                  "status": "complete",
                  "end_customer_id": null
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Answer within 15 seconds; any other answer, or none, is retried over about three days."
          }
        }
      }
    }
  }
}
