{
  "openapi": "3.1.1",
  "info": {
    "title": "OHS Platform API",
    "version": "0.3.0",
    "description": "Preview API products for public business enquiries and approved server integrations. Published catalogue metadata only; no clinical, staff or private customer data. Machine access uses OAuth 2.0 client credentials with explicitly approved scopes. OIDC and user delegation are not enabled.",
    "contact": {
      "name": "OHS support",
      "email": "contact@opeshealthsystems.com"
    }
  },
  "servers": [
    {
      "url": "/api/v1"
    }
  ],
  "security": [],
  "tags": [
    {
      "name": "Enquiries",
      "description": "Submit business enquiries to OHS."
    },
    {
      "name": "Identity",
      "description": "Approved machine-to-machine access only."
    },
    {
      "name": "Catalogue",
      "description": "Published software product metadata."
    }
  ],
  "paths": {
    "/enquiries": {
      "post": {
        "operationId": "submitEnquiry",
        "tags": [
          "Enquiries"
        ],
        "summary": "Record a website enquiry and attempt immediate email delivery",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EnquiryInput"
              },
              "example": {
                "name": "Example sender",
                "email": "sender@example.test",
                "message": "Please share information about OHS products.",
                "locale": "en"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Enquiry recorded with durable delivery intent. sent means SMTP acceptance, not confirmed inbox placement. pending includes retryable or ambiguous delivery requiring operational recovery. A replay returns the saved original response, not current delivery status. Scheduled recovery requires a hosting scheduler.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated correlation identifier.",
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              },
              "Idempotency-Replayed": {
                "description": "Present when an idempotency key was supplied: true for replay, false for first submission.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true",
                    "false"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "status",
                        "delivery_status"
                      ],
                      "properties": {
                        "status": {
                          "const": "recorded",
                          "type": "string"
                        },
                        "delivery_status": {
                          "type": "string",
                          "enum": [
                            "sent",
                            "pending"
                          ]
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown API route.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated correlation identifier.",
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated correlation identifier.",
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "JSON request body required.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated correlation identifier.",
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Request validation failed.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated correlation identifier.",
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Submission quota exceeded: 5 per minute and 30 per day per source IP.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated correlation identifier.",
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              },
              "Retry-After": {
                "description": "Seconds until retry is permitted.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server failure. Retry only with the same Idempotency-Key and identical input; without a key an ambiguous retry can create another enquiry.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated correlation identifier.",
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency key was already used with different input, or an identical request is still processing. Retry-After is present only for a processing conflict. No new enquiry is created.",
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              },
              "Retry-After": {
                "description": "For an identical in-flight request only, wait this many seconds before retrying the same key/input.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Recommended unique random key (for example UUIDv4). Scoped to this anonymous endpoint globally, not by IP. Retained for 24 hours from the first accepted request. Identical normalized input replays the saved 201 response without creating or sending again. Different input returns 409. An in-flight request returns 409 with Retry-After. Reuse after expiry can create a new enquiry. Without a key, requests are not deduplicated.",
            "schema": {
              "type": "string",
              "minLength": 16,
              "maxLength": 128,
              "pattern": "^[A-Za-z0-9._:-]+$"
            }
          }
        ]
      }
    },
    "/oauth/token": {
      "post": {
        "operationId": "issueMachineToken",
        "tags": [
          "Identity"
        ],
        "summary": "Exchange reviewed machine credentials for a 15-minute token",
        "security": [],
        "description": "Only client_credentials is enabled. Request explicit approved scopes. No refresh token is issued. Credentials must be kept on a server. Limit: 20 attempts per minute per source IP.",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "grant_type",
                  "client_id",
                  "client_secret"
                ],
                "properties": {
                  "grant_type": {
                    "type": "string",
                    "const": "client_credentials"
                  },
                  "client_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "client_secret": {
                    "type": "string",
                    "writeOnly": true
                  },
                  "scope": {
                    "type": "string",
                    "example": "catalogue:read integration:read"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Access token issued. No refresh token or ID token is returned.",
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthToken"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request/scope or unsupported grant. OAuth error format applies to token endpoint errors.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or unapproved client credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          },
          "429": {
            "description": "Token request rate exceeded.",
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/catalogue/products": {
      "get": {
        "operationId": "listCatalogueProducts",
        "tags": [
          "Catalogue"
        ],
        "security": [
          {
            "OAuth2": [
              "catalogue:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 10000,
              "default": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          },
          {
            "name": "locale",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "fr"
              ],
              "default": "en"
            }
          }
        ],
        "responses": {
          "401": {
            "description": "Missing, invalid, revoked or expired machine access; disabled owner or revoked client.",
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              },
              "WWW-Authenticate": {
                "schema": {
                  "const": "Bearer",
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Required scope is absent from token or current client approval.",
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid pagination or locale.",
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "60 authenticated attempts per minute per client exceeded.",
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "200": {
            "description": "Published product metadata; bounded pagination.",
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CatalogueProduct"
                      }
                    },
                    "meta": {
                      "type": "object",
                      "required": [
                        "page",
                        "per_page",
                        "total"
                      ],
                      "properties": {
                        "page": {
                          "type": "integer",
                          "minimum": 1
                        },
                        "per_page": {
                          "type": "integer",
                          "minimum": 1,
                          "maximum": 50
                        },
                        "total": {
                          "type": "integer",
                          "minimum": 0
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/integration": {
      "get": {
        "operationId": "inspectIntegration",
        "tags": [
          "Identity"
        ],
        "security": [
          {
            "OAuth2": [
              "integration:read"
            ]
          }
        ],
        "responses": {
          "401": {
            "description": "Missing, invalid, revoked or expired machine access; disabled owner or revoked client.",
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              },
              "WWW-Authenticate": {
                "schema": {
                  "const": "Bearer",
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Required scope is absent from token or current client approval.",
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "60 authenticated attempts per minute per client exceeded.",
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "200": {
            "description": "Identity and authenticated attempts for this token client only. Counter includes the current call and resets on UTC date boundaries.",
            "headers": {
              "X-Request-ID": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "client_id",
                        "name",
                        "scopes",
                        "requests_today"
                      ],
                      "properties": {
                        "client_id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "name": {
                          "type": "string"
                        },
                        "scopes": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "requests_today": {
                          "type": "integer",
                          "minimum": 1
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "EnquiryInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "email"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "email": {
            "type": "string",
            "format": "email",
            "maxLength": 150
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 30
          },
          "facility_type": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 60
          },
          "products": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 255
          },
          "message": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 2000
          },
          "product_slug": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 60
          },
          "locale": {
            "type": "string",
            "enum": [
              "en",
              "fr"
            ],
            "default": "en"
          }
        }
      },
      "Problem": {
        "type": "object",
        "required": [
          "type",
          "title",
          "status",
          "request_id"
        ],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "request_id": {
            "type": "string",
            "format": "uuid"
          },
          "errors": {
            "type": "object",
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        }
      },
      "OAuthError": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string"
          },
          "error_description": {
            "type": "string"
          },
          "hint": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "OAuthToken": {
        "type": "object",
        "required": [
          "access_token",
          "token_type",
          "expires_in"
        ],
        "properties": {
          "access_token": {
            "type": "string"
          },
          "token_type": {
            "const": "Bearer",
            "type": "string"
          },
          "expires_in": {
            "type": "integer",
            "minimum": 1,
            "maximum": 900
          }
        }
      },
      "CatalogueProduct": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "slug",
          "name",
          "category",
          "url"
        ],
        "properties": {
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "category": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          }
        }
      }
    },
    "securitySchemes": {
      "OAuth2": {
        "type": "oauth2",
        "flows": {
          "clientCredentials": {
            "tokenUrl": "/api/v1/oauth/token",
            "scopes": {
              "catalogue:read": "Read published product metadata",
              "integration:read": "Inspect this integration identity and usage"
            }
          }
        }
      }
    }
  }
}
