openapi: 3.1.0 info: title: Patcher Public API version: 1.0.0-preview description: > Preview contract for the key-required, read-only Public Open API. The local Worker implementation accepts GET and HEAD for every documented route; HEAD follows the same authentication, quota, cache, ETag, and error pipeline as GET but returns no body. license: name: CC BY 4.0 identifier: CC-BY-4.0 servers: - url: https://api.patcher.xyz/v1 security: - apiKey: [] paths: /modules: get: operationId: listModules summary: List public modules description: Any method other than GET, HEAD, or OPTIONS returns 405 method_not_allowed. parameters: - $ref: "#/components/parameters/Cursor" - $ref: "#/components/parameters/FieldsModule" - $ref: "#/components/parameters/Limit" - $ref: "#/components/parameters/Sort" - $ref: "#/components/parameters/QUnsupported" - $ref: "#/components/parameters/ManufacturerIdFilter" - $ref: "#/components/parameters/HpFilter" - $ref: "#/components/parameters/StandardFilter" - $ref: "#/components/parameters/TagFilter" - $ref: "#/components/parameters/ModuleInclude" responses: "200": description: Paginated public modules headers: &SuccessHeaders ETag: $ref: "#/components/headers/ETag" X-Cache: $ref: "#/components/headers/XCache" X-Request-ID: $ref: "#/components/headers/XRequestId" X-RateLimit-Limit-Month: $ref: "#/components/headers/XRateLimitLimitMonth" X-RateLimit-Remaining-Month: $ref: "#/components/headers/XRateLimitRemainingMonth" X-RateLimit-Limit-Minute: $ref: "#/components/headers/XRateLimitLimitMinute" X-RateLimit-Remaining-Minute: $ref: "#/components/headers/XRateLimitRemainingMinute" X-RateLimit-Reset: $ref: "#/components/headers/XRateLimitReset" content: application/json: schema: $ref: "#/components/schemas/ModuleListResponse" "304": $ref: "#/components/responses/NotModified" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "429": $ref: "#/components/responses/RateLimited" "503": $ref: "#/components/responses/ServiceUnavailable" "405": $ref: "#/components/responses/MethodNotAllowed" head: operationId: headModules summary: Fetch headers for the public modules list parameters: - $ref: "#/components/parameters/Cursor" - $ref: "#/components/parameters/FieldsModule" - $ref: "#/components/parameters/Limit" - $ref: "#/components/parameters/Sort" - $ref: "#/components/parameters/QUnsupported" - $ref: "#/components/parameters/ManufacturerIdFilter" - $ref: "#/components/parameters/HpFilter" - $ref: "#/components/parameters/StandardFilter" - $ref: "#/components/parameters/TagFilter" - $ref: "#/components/parameters/ModuleInclude" responses: "200": $ref: "#/components/responses/HeadOk" "304": $ref: "#/components/responses/NotModified" "400": $ref: "#/components/responses/HeadBadRequest" "401": $ref: "#/components/responses/HeadUnauthorized" "429": $ref: "#/components/responses/HeadRateLimited" "503": $ref: "#/components/responses/HeadServiceUnavailable" /modules/{id}: get: operationId: getModule summary: Get a public module description: Any method other than GET, HEAD, or OPTIONS returns 405 method_not_allowed. parameters: - $ref: "#/components/parameters/IntegerId" - $ref: "#/components/parameters/FieldsModule" - $ref: "#/components/parameters/ModuleInclude" responses: "200": description: Public module headers: *SuccessHeaders content: application/json: schema: $ref: "#/components/schemas/ModuleDetailResponse" "304": $ref: "#/components/responses/NotModified" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" "429": $ref: "#/components/responses/RateLimited" "503": $ref: "#/components/responses/ServiceUnavailable" "405": $ref: "#/components/responses/MethodNotAllowed" head: operationId: headModule summary: Fetch headers for a public module parameters: - $ref: "#/components/parameters/IntegerId" - $ref: "#/components/parameters/FieldsModule" - $ref: "#/components/parameters/ModuleInclude" responses: "200": $ref: "#/components/responses/HeadOk" "304": $ref: "#/components/responses/NotModified" "400": $ref: "#/components/responses/HeadBadRequest" "401": $ref: "#/components/responses/HeadUnauthorized" "404": $ref: "#/components/responses/HeadNotFound" "429": $ref: "#/components/responses/HeadRateLimited" "503": $ref: "#/components/responses/HeadServiceUnavailable" /manufacturers: get: operationId: listManufacturers summary: List manufacturers with public modules description: Any method other than GET, HEAD, or OPTIONS returns 405 method_not_allowed. parameters: - $ref: "#/components/parameters/Cursor" - $ref: "#/components/parameters/FieldsManufacturer" - $ref: "#/components/parameters/Limit" - $ref: "#/components/parameters/Sort" - $ref: "#/components/parameters/QUnsupported" responses: "200": description: Paginated public manufacturers headers: *SuccessHeaders content: application/json: schema: $ref: "#/components/schemas/ManufacturerListResponse" "304": $ref: "#/components/responses/NotModified" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "429": $ref: "#/components/responses/RateLimited" "503": $ref: "#/components/responses/ServiceUnavailable" "405": $ref: "#/components/responses/MethodNotAllowed" head: operationId: headManufacturers summary: Fetch headers for the public manufacturers list parameters: - $ref: "#/components/parameters/Cursor" - $ref: "#/components/parameters/FieldsManufacturer" - $ref: "#/components/parameters/Limit" - $ref: "#/components/parameters/Sort" - $ref: "#/components/parameters/QUnsupported" responses: "200": $ref: "#/components/responses/HeadOk" "304": $ref: "#/components/responses/NotModified" "400": $ref: "#/components/responses/HeadBadRequest" "401": $ref: "#/components/responses/HeadUnauthorized" "429": $ref: "#/components/responses/HeadRateLimited" "503": $ref: "#/components/responses/HeadServiceUnavailable" /manufacturers/{id}: get: operationId: getManufacturer summary: Get a public manufacturer description: Any method other than GET, HEAD, or OPTIONS returns 405 method_not_allowed. parameters: - $ref: "#/components/parameters/IntegerId" - $ref: "#/components/parameters/FieldsManufacturer" - $ref: "#/components/parameters/ManufacturerInclude" responses: "200": description: Public manufacturer headers: *SuccessHeaders content: application/json: schema: $ref: "#/components/schemas/ManufacturerDetailResponse" "304": $ref: "#/components/responses/NotModified" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" "429": $ref: "#/components/responses/RateLimited" "503": $ref: "#/components/responses/ServiceUnavailable" "405": $ref: "#/components/responses/MethodNotAllowed" head: operationId: headManufacturer summary: Fetch headers for a public manufacturer parameters: - $ref: "#/components/parameters/IntegerId" - $ref: "#/components/parameters/FieldsManufacturer" - $ref: "#/components/parameters/ManufacturerInclude" responses: "200": $ref: "#/components/responses/HeadOk" "304": $ref: "#/components/responses/NotModified" "400": $ref: "#/components/responses/HeadBadRequest" "401": $ref: "#/components/responses/HeadUnauthorized" "404": $ref: "#/components/responses/HeadNotFound" "429": $ref: "#/components/responses/HeadRateLimited" "503": $ref: "#/components/responses/HeadServiceUnavailable" /standards: get: operationId: listStandards summary: List public standards description: Any method other than GET, HEAD, or OPTIONS returns 405 method_not_allowed. parameters: - $ref: "#/components/parameters/Cursor" - $ref: "#/components/parameters/FieldsStandard" - $ref: "#/components/parameters/Limit" - $ref: "#/components/parameters/Sort" - $ref: "#/components/parameters/QUnsupported" responses: "200": description: Paginated public standards headers: *SuccessHeaders content: application/json: schema: $ref: "#/components/schemas/StandardListResponse" "304": $ref: "#/components/responses/NotModified" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "429": $ref: "#/components/responses/RateLimited" "503": $ref: "#/components/responses/ServiceUnavailable" "405": $ref: "#/components/responses/MethodNotAllowed" head: operationId: headStandards summary: Fetch headers for public standards parameters: - $ref: "#/components/parameters/Cursor" - $ref: "#/components/parameters/FieldsStandard" - $ref: "#/components/parameters/Limit" - $ref: "#/components/parameters/Sort" - $ref: "#/components/parameters/QUnsupported" responses: "200": $ref: "#/components/responses/HeadOk" "304": $ref: "#/components/responses/NotModified" "400": $ref: "#/components/responses/HeadBadRequest" "401": $ref: "#/components/responses/HeadUnauthorized" "429": $ref: "#/components/responses/HeadRateLimited" "503": $ref: "#/components/responses/HeadServiceUnavailable" /tags: get: operationId: listTags summary: List public tags description: Any method other than GET, HEAD, or OPTIONS returns 405 method_not_allowed. parameters: - $ref: "#/components/parameters/Cursor" - $ref: "#/components/parameters/FieldsTag" - $ref: "#/components/parameters/Limit" - $ref: "#/components/parameters/Sort" - $ref: "#/components/parameters/QUnsupported" responses: "200": description: Paginated public tags headers: *SuccessHeaders content: application/json: schema: $ref: "#/components/schemas/TagListResponse" "304": $ref: "#/components/responses/NotModified" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "429": $ref: "#/components/responses/RateLimited" "503": $ref: "#/components/responses/ServiceUnavailable" "405": $ref: "#/components/responses/MethodNotAllowed" head: operationId: headTags summary: Fetch headers for public tags parameters: - $ref: "#/components/parameters/Cursor" - $ref: "#/components/parameters/FieldsTag" - $ref: "#/components/parameters/Limit" - $ref: "#/components/parameters/Sort" - $ref: "#/components/parameters/QUnsupported" responses: "200": $ref: "#/components/responses/HeadOk" "304": $ref: "#/components/responses/NotModified" "400": $ref: "#/components/responses/HeadBadRequest" "401": $ref: "#/components/responses/HeadUnauthorized" "429": $ref: "#/components/responses/HeadRateLimited" "503": $ref: "#/components/responses/HeadServiceUnavailable" components: securitySchemes: apiKey: type: http scheme: bearer bearerFormat: pk_live_<22 base64url characters> parameters: IntegerId: name: id in: path required: true schema: type: integer minimum: 1 Cursor: name: cursor in: query schema: type: string description: Base64url cursor of {"v":1,"s":last_sort_value,"id":last_id}. Limit: name: limit in: query schema: type: integer minimum: 1 maximum: 100 default: 50 Sort: name: sort in: query schema: type: string enum: [name, id] default: name description: Ascending sort; id is always the tie-break for name sort. QUnsupported: name: q in: query schema: type: string description: Reserved for future trigram search. Supplying q returns 400 unsupported_parameter in v1 MVP. FieldsModule: name: fields in: query schema: type: string description: Comma-separated module top-level fields; id is always retained. FieldsManufacturer: name: fields in: query schema: type: string description: Comma-separated manufacturer top-level fields; id is always retained. FieldsStandard: name: fields in: query schema: type: string description: Comma-separated standard fields (id,name); id is always retained. FieldsTag: name: fields in: query schema: type: string description: Comma-separated tag fields (id,name,type); id is always retained. ManufacturerIdFilter: name: manufacturer_id in: query schema: type: integer minimum: 1 HpFilter: name: hp in: query schema: type: integer minimum: 1 StandardFilter: name: standard in: query schema: type: integer minimum: 0 description: Standard ID. Standard 0 is the 3U format. TagFilter: name: tag in: query schema: type: integer minimum: 1 ModuleInclude: name: include in: query schema: type: string enum: [ins, outs, tags, panels, "ins,outs,tags,panels"] description: "Comma-separated module includes: ins, outs, tags, panels." ManufacturerInclude: name: include in: query schema: type: string enum: [modules] description: Manufacturer detail only; includes safe module summaries. headers: ETag: schema: type: string description: Quoted SHA-256 of the exact JSON response bytes. XCache: schema: type: string enum: [HIT, MISS, STALE] XRequestId: schema: type: string description: Per-request UUID emitted on success and error responses. XRateLimitLimitMonth: schema: type: integer description: Effective monthly request quota for the authenticated API key. Present only after quota has been consumed. XRateLimitRemainingMonth: schema: type: integer description: Requests remaining in the current monthly window for the authenticated API key. Present only after quota has been consumed. XRateLimitLimitMinute: schema: type: integer description: Effective per-minute request quota for the authenticated API key. Present only after quota has been consumed. XRateLimitRemainingMinute: schema: type: integer description: Requests remaining in the current minute window for the authenticated API key. Present only after quota has been consumed. XRateLimitReset: schema: type: string format: date-time description: Current minute-window start timestamp emitted by the MVP Worker. Despite the header name, this is not the next reset time. RetryAfter: schema: type: integer description: Seconds until the blocked minute or month quota window can accept another request. Allow: schema: type: string description: Comma-separated list of methods accepted by this route, returned with 405 responses. responses: HeadOk: description: Headers for the matching GET response; no response body. headers: *SuccessHeaders HeadBadRequest: description: Invalid, unknown, or unsupported query parameter; no response body for HEAD. headers: X-Request-ID: $ref: "#/components/headers/XRequestId" HeadUnauthorized: description: Missing, malformed, unknown, or revoked API key; no response body for HEAD. headers: X-Request-ID: $ref: "#/components/headers/XRequestId" HeadNotFound: description: Resource not found; no response body for HEAD. headers: X-Request-ID: $ref: "#/components/headers/XRequestId" X-RateLimit-Limit-Month: $ref: "#/components/headers/XRateLimitLimitMonth" X-RateLimit-Remaining-Month: $ref: "#/components/headers/XRateLimitRemainingMonth" X-RateLimit-Limit-Minute: $ref: "#/components/headers/XRateLimitLimitMinute" X-RateLimit-Remaining-Minute: $ref: "#/components/headers/XRateLimitRemainingMinute" X-RateLimit-Reset: $ref: "#/components/headers/XRateLimitReset" HeadRateLimited: description: API key quota exceeded; no response body for HEAD. headers: Retry-After: $ref: "#/components/headers/RetryAfter" X-Request-ID: $ref: "#/components/headers/XRequestId" X-RateLimit-Limit-Month: $ref: "#/components/headers/XRateLimitLimitMonth" X-RateLimit-Remaining-Month: $ref: "#/components/headers/XRateLimitRemainingMonth" X-RateLimit-Limit-Minute: $ref: "#/components/headers/XRateLimitLimitMinute" X-RateLimit-Remaining-Minute: $ref: "#/components/headers/XRateLimitRemainingMinute" X-RateLimit-Reset: $ref: "#/components/headers/XRateLimitReset" HeadServiceUnavailable: description: Service unavailable; no response body for HEAD. headers: X-Request-ID: $ref: "#/components/headers/XRequestId" X-RateLimit-Limit-Month: $ref: "#/components/headers/XRateLimitLimitMonth" X-RateLimit-Remaining-Month: $ref: "#/components/headers/XRateLimitRemainingMonth" X-RateLimit-Limit-Minute: $ref: "#/components/headers/XRateLimitLimitMinute" X-RateLimit-Remaining-Minute: $ref: "#/components/headers/XRateLimitRemainingMinute" X-RateLimit-Reset: $ref: "#/components/headers/XRateLimitReset" NotModified: description: ETag matched; no body. Quota and cache headers still reflect this request. headers: *SuccessHeaders BadRequest: description: Invalid, unknown, or unsupported query parameter. headers: &PreQuotaErrorHeaders X-Request-ID: $ref: "#/components/headers/XRequestId" content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" Unauthorized: description: Missing, malformed, unknown, or revoked API key. headers: *PreQuotaErrorHeaders content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" NotFound: description: Resource not found. headers: &PostQuotaErrorHeaders X-Request-ID: $ref: "#/components/headers/XRequestId" X-RateLimit-Limit-Month: $ref: "#/components/headers/XRateLimitLimitMonth" X-RateLimit-Remaining-Month: $ref: "#/components/headers/XRateLimitRemainingMonth" X-RateLimit-Limit-Minute: $ref: "#/components/headers/XRateLimitLimitMinute" X-RateLimit-Remaining-Minute: $ref: "#/components/headers/XRateLimitRemainingMinute" X-RateLimit-Reset: $ref: "#/components/headers/XRateLimitReset" content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" RateLimited: description: API key quota exceeded. headers: Retry-After: $ref: "#/components/headers/RetryAfter" X-Request-ID: $ref: "#/components/headers/XRequestId" X-RateLimit-Limit-Month: $ref: "#/components/headers/XRateLimitLimitMonth" X-RateLimit-Remaining-Month: $ref: "#/components/headers/XRateLimitRemainingMonth" X-RateLimit-Limit-Minute: $ref: "#/components/headers/XRateLimitLimitMinute" X-RateLimit-Remaining-Minute: $ref: "#/components/headers/XRateLimitRemainingMinute" X-RateLimit-Reset: $ref: "#/components/headers/XRateLimitReset" content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" ServiceUnavailable: description: > Service unavailable. The Worker may emit configuration_error, authentication_unavailable, quota_unavailable, or origin_unavailable. Quota headers are present only when quota was consumed before the 503 (for example, origin_unavailable). headers: *PostQuotaErrorHeaders content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" MethodNotAllowed: description: > Only GET, HEAD, and OPTIONS are supported on this route. Returned before authentication and quota consumption, so no rate-limit headers are attached. headers: Allow: $ref: "#/components/headers/Allow" X-Request-ID: $ref: "#/components/headers/XRequestId" content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" schemas: Page: type: object required: [next_cursor] properties: next_cursor: type: [string, "null"] ErrorResponse: type: object required: [error] properties: error: type: object required: [code, message, request_id] properties: code: type: string enum: [unknown_parameter, invalid_parameter, unsupported_parameter, missing_authorization, malformed_authorization, invalid_key, not_found, rate_limit_exceeded, origin_unavailable, authentication_unavailable, quota_unavailable, configuration_error, method_not_allowed] message: type: string request_id: type: string ModuleListResponse: type: object required: [data, page] properties: data: type: array items: $ref: "#/components/schemas/Module" page: $ref: "#/components/schemas/Page" ModuleDetailResponse: type: object required: [data] properties: data: $ref: "#/components/schemas/Module" ManufacturerListResponse: type: object required: [data, page] properties: data: type: array items: $ref: "#/components/schemas/Manufacturer" page: $ref: "#/components/schemas/Page" ManufacturerDetailResponse: type: object required: [data] properties: data: $ref: "#/components/schemas/Manufacturer" StandardListResponse: type: object required: [data, page] properties: data: type: array items: $ref: "#/components/schemas/Standard" page: $ref: "#/components/schemas/Page" TagListResponse: type: object required: [data, page] properties: data: type: array items: $ref: "#/components/schemas/Tag" page: $ref: "#/components/schemas/Page" Module: type: object required: [id] properties: id: type: integer name: type: string description: type: [string, "null"] hp: type: [integer, "null"] standard: type: [integer, "null"] manufacturer_id: type: [integer, "null"] depth: type: [number, "null"] depth_max: type: [number, "null"] is_diy: type: [boolean, "null"] manual_url: type: [string, "null"] power_neg_12: type: [number, "null"] power_pos_12: type: [number, "null"] power_pos_5: type: [number, "null"] switches: oneOf: - type: array items: $ref: "#/components/schemas/ModuleSwitch" - type: "null" weight: type: [number, "null"] ins: type: array items: $ref: "#/components/schemas/Port" outs: type: array items: $ref: "#/components/schemas/Port" tags: type: array items: $ref: "#/components/schemas/Tag" panels: type: array items: $ref: "#/components/schemas/Panel" ModuleSwitch: type: object required: [name, positions] properties: name: type: string positions: type: array items: type: string Manufacturer: type: object required: [id] properties: id: type: integer name: type: string description: type: [string, "null"] tagline: type: [string, "null"] website_url: type: [string, "null"] social_links: {} logo: type: [string, "null"] modules: type: array items: $ref: "#/components/schemas/Module" Standard: type: object required: [id, name] properties: id: type: integer minimum: 0 name: type: string Tag: type: object required: [id, name] properties: id: type: integer name: type: string type: type: [string, "null"] enum: [nature, character, voice, source, filter, modulation, effect, sequencing, utility, blank, null] Port: type: object required: [id, name] properties: id: type: integer name: type: string is_audio: type: [boolean, "null"] is_dcc: type: [boolean, "null"] is_voct: type: [boolean, "null"] min: type: [number, "null"] max: type: [number, "null"] Panel: type: object required: [id] properties: id: type: integer color: type: [string, "null"] description: type: [string, "null"]