{
  "openapi": "3.0.3",
  "info": {
    "title": "Trimedixs CRM-API",
    "version": "1.0.0",
    "description": "Read-only training API with 232 fictional healthcare organizations in the Netherlands. No authentication or HR data. Search for a customer and store its customerId and companyName with an expense claim in your own app. Unknown API routes return 404; write requests (POST, PUT, PATCH, DELETE) return 405 with Allow: GET, OPTIONS. Errors use the Error schema. CORS allows cross-origin requests; do not send credentials."
  },
  "servers": [
    {
      "url": "/",
      "description": "The API serving this documentation (local or deployed)"
    }
  ],
  "security": [],
  "tags": [
    {
      "name": "Customers"
    },
    {
      "name": "Status"
    },
    {
      "name": "Examples"
    },
    {
      "name": "CORS"
    }
  ],
  "paths": {
    "/api/v1/customers": {
      "get": {
        "tags": [
          "Customers"
        ],
        "operationId": "searchCustomers",
        "summary": "Search healthcare organizations by company name or customer ID",
        "description": "Case-insensitive search across 232 fictional healthcare organizations, matching companyName or customerId, not city. Provide search exactly once; leading and trailing whitespace is trimmed. Returns up to 20 matches ordered by customerId. Refine the search when total exceeds 20. Pagination is not supported.",
        "parameters": [
          {
            "name": "search",
            "in": "query",
            "required": true,
            "description": "Part of the company name or customer ID; 2–100 characters after trimming.",
            "schema": {
              "type": "string",
              "minLength": 2,
              "maxLength": 100
            },
            "examples": {
              "huisarts": {
                "value": "huisarts"
              },
              "customerId": {
                "value": "K-1001"
              },
              "specialCharacters": {
                "value": "Hart & Hand"
              },
              "noMatches": {
                "value": "bestaatniet"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Search results; no matches returns an empty customers array.",
            "headers": {
              "Access-Control-Allow-Origin": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "*"
                  ]
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "no-store"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchResult"
                },
                "example": {
                  "customers": [
                    {
                      "customerId": "K-1001",
                      "companyName": "Zorgcentrum Lindevaert",
                      "city": "Breda"
                    }
                  ],
                  "total": 1,
                  "limit": 20
                }
              }
            }
          },
          "400": {
            "description": "The search parameter is missing, too short, too long or provided more than once.",
            "headers": {
              "Access-Control-Allow-Origin": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "*"
                  ]
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "no-store"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "INVALID_SEARCH",
                    "message": "Provide exactly one search parameter containing 2 to 100 characters."
                  }
                }
              }
            }
          }
        }
      },
      "options": {
        "tags": [
          "CORS"
        ],
        "operationId": "preflightSearchCustomers",
        "summary": "CORS preflight (performed by the browser)",
        "description": "Browsers send this request automatically when needed. You do not need to call it manually for a regular GET request.",
        "responses": {
          "204": {
            "description": "No content; the allowed methods and headers are returned.",
            "headers": {
              "Access-Control-Allow-Origin": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "*"
                  ]
                }
              },
              "Access-Control-Allow-Methods": {
                "schema": {
                  "type": "string",
                  "example": "GET, OPTIONS"
                }
              },
              "Access-Control-Allow-Headers": {
                "schema": {
                  "type": "string",
                  "example": "Accept, Content-Type"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/status": {
      "get": {
        "tags": [
          "Status"
        ],
        "operationId": "getStatus",
        "summary": "Check the API connection",
        "responses": {
          "200": {
            "description": "The API is available and the data is fictional.",
            "headers": {
              "Access-Control-Allow-Origin": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "*"
                  ]
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "no-store"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Status"
                },
                "example": {
                  "status": "ok",
                  "system": "Trimedixs CRM training API",
                  "version": "1",
                  "fictional": true
                }
              }
            }
          }
        }
      },
      "options": {
        "tags": [
          "CORS"
        ],
        "operationId": "preflightGetStatus",
        "summary": "CORS preflight (performed by the browser)",
        "description": "Browsers send this request automatically when needed. You do not need to call it manually for a regular GET request.",
        "responses": {
          "204": {
            "description": "No content; the allowed methods and headers are returned.",
            "headers": {
              "Access-Control-Allow-Origin": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "*"
                  ]
                }
              },
              "Access-Control-Allow-Methods": {
                "schema": {
                  "type": "string",
                  "example": "GET, OPTIONS"
                }
              },
              "Access-Control-Allow-Headers": {
                "schema": {
                  "type": "string",
                  "example": "Accept, Content-Type"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/examples/error": {
      "get": {
        "tags": [
          "Examples"
        ],
        "operationId": "getExampleError",
        "summary": "Practice error handling with an intentional 503 response",
        "description": "This endpoint always returns an error. The other endpoints remain available.",
        "responses": {
          "503": {
            "description": "Intentional example error; this does not mean the entire API is unavailable.",
            "headers": {
              "Access-Control-Allow-Origin": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "*"
                  ]
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "no-store"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "EXAMPLE_ERROR",
                    "message": "This is an intentional example error. Try the customer search endpoint next."
                  }
                }
              }
            }
          }
        }
      },
      "options": {
        "tags": [
          "CORS"
        ],
        "operationId": "preflightGetExampleError",
        "summary": "CORS preflight (performed by the browser)",
        "description": "Browsers send this request automatically when needed. You do not need to call it manually for a regular GET request.",
        "responses": {
          "204": {
            "description": "No content; the allowed methods and headers are returned.",
            "headers": {
              "Access-Control-Allow-Origin": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "*"
                  ]
                }
              },
              "Access-Control-Allow-Methods": {
                "schema": {
                  "type": "string",
                  "example": "GET, OPTIONS"
                }
              },
              "Access-Control-Allow-Headers": {
                "schema": {
                  "type": "string",
                  "example": "Accept, Content-Type"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Customer": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "customerId",
          "companyName",
          "city"
        ],
        "properties": {
          "customerId": {
            "type": "string",
            "pattern": "^K-[0-9]{4}$",
            "example": "K-1001"
          },
          "companyName": {
            "type": "string",
            "example": "Zorgcentrum Lindevaert"
          },
          "city": {
            "type": "string",
            "example": "Breda"
          }
        }
      },
      "SearchResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "customers",
          "total",
          "limit"
        ],
        "properties": {
          "customers": {
            "type": "array",
            "maxItems": 20,
            "items": {
              "$ref": "#/components/schemas/Customer"
            }
          },
          "total": {
            "type": "integer",
            "minimum": 0,
            "description": "The total number of matches, including those beyond the first 20 returned."
          },
          "limit": {
            "type": "integer",
            "enum": [
              20
            ]
          }
        }
      },
      "Status": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "status",
          "system",
          "version",
          "fictional"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ok"
            ]
          },
          "system": {
            "type": "string"
          },
          "version": {
            "type": "string",
            "enum": [
              "1"
            ]
          },
          "fictional": {
            "type": "boolean",
            "enum": [
              true
            ]
          }
        }
      },
      "Error": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      }
    }
  }
}
