{
  "info": {
    "_postman_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "name": "DentiSoft Gateway API",
    "description": "# DentiSoft Gateway API\n\nAPI de DentiSoft para integraciones externas y el portal de pacientes.\n\n## Autenticación\n\n### API Key (`X-API-Key`)\nRequerida en todos los endpoints protegidos. Obtén tu clave desde el panel de administración de DentiSoft:\n```\nX-API-Key: ds_live_your_key_here\n```\n\n### Patient JWT (`X-Patient-Token`)\nObtenido tras completar el flujo de verificación OTP. Válido por 24h.\n```\nX-Patient-Token: eyJhbGciOiJIUzI1NiIs...\n```\n\n## Rate Limits\n| Plan       | Requests / 15 min |\n|------------|------------------|\n| free       | 200              |\n| pro        | 1 000            |\n| enterprise | 5 000            |\n\n## Base URL\n`{{baseUrl}}`",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "variable": [
    {
      "key": "baseUrl",
      "value": "https://api-dentisoft.clicfstudios.com",
      "type": "string"
    },
    {
      "key": "apiKey",
      "value": "YOUR_API_KEY_HERE",
      "type": "string",
      "description": "Replace with your API key from Configuración > API Keys"
    },
    {
      "key": "patientToken",
      "value": "",
      "type": "string"
    },
    {
      "key": "verificationToken",
      "value": "",
      "type": "string",
      "description": "JWT de 15 min obtenido en verify-otp. Usar en set-password."
    },
    {
      "key": "clinicSlug",
      "value": "mi-clinica",
      "type": "string"
    },
    {
      "key": "patientEmail",
      "value": "paciente@example.com",
      "type": "string"
    }
  ],
  "item": [
    {
      "name": "Health",
      "description": "Observability & liveness endpoints. No authentication required.",
      "item": [
        {
          "name": "Gateway + Core Health",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/health",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "health"
              ]
            },
            "description": "Returns gateway status and performs a liveness check against Core API.\n\n**Response 200 — both healthy**\n```json\n{\n  \"service\": \"dentisoft-gateway\",\n  \"status\": \"ok\",\n  \"ts\": \"2026-04-02T18:00:00.000Z\",\n  \"requestId\": \"req_abc123\",\n  \"core\": { \"status\": \"ok\", \"ms\": 12 }\n}\n```\n\n**Response 503 — core unreachable**\n```json\n{ \"status\": \"degraded\", \"core\": { \"status\": \"unreachable\" } }\n```"
          },
          "response": [
            {
              "name": "200 OK — Healthy",
              "status": "OK",
              "code": 200,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"service\": \"dentisoft-gateway\",\n  \"status\": \"ok\",\n  \"ts\": \"2026-04-02T18:00:00.000Z\",\n  \"requestId\": \"req_abc123\",\n  \"core\": { \"status\": \"ok\", \"ms\": 12 }\n}"
            },
            {
              "name": "503 Degraded — Core Unreachable",
              "status": "Service Unavailable",
              "code": 503,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"service\": \"dentisoft-gateway\",\n  \"status\": \"degraded\",\n  \"core\": { \"status\": \"unreachable\" }\n}"
            }
          ]
        },
        {
          "name": "Readiness Probe",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/health/ready",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "health",
                "ready"
              ]
            },
            "description": "Kubernetes readiness probe — only checks that the gateway process is running. Does **not** check Core."
          },
          "response": [
            {
              "name": "200 Ready",
              "status": "OK",
              "code": 200,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{ \"status\": \"ready\" }"
            }
          ]
        }
      ]
    },
    {
      "name": "Patient Portal",
      "description": "Endpoints para la app móvil de pacientes.\n\n**Flujo de registro (primera vez):**\n1. `POST /patient/auth/register` — cédula + celular + clinicSlug → envía OTP\n2. `POST /patient/auth/verify-otp` — cédula + clinicSlug + code → verificationToken\n3. `POST /patient/auth/set-password` — verificationToken + password → sessionToken\n\n**Login (cuentas ya registradas):**\n- `POST /patient/auth/login` — cédula o correo + contraseña (sin clinicSlug)\n\nLos endpoints protegidos requieren `X-Patient-Token` en el header.",
      "item": [
        {
          "name": "Auth",
          "item": [
            {
              "name": "1. Register (cédula + celular + clínica)",
              "event": [
                {
                  "listen": "test",
                  "script": {
                    "exec": [
                      "pm.test('Status is 200', () => pm.response.to.have.status(200));",
                      "pm.test('OTP enviado', () => {",
                      "  const body = pm.response.json();",
                      "  pm.expect(body.success).to.be.true;",
                      "  pm.expect(body.otpSent).to.be.true;",
                      "});"
                    ],
                    "type": "text/javascript"
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"cedula\":     \"1234567890\",\n  \"phone\":      \"+593987654321\",\n  \"clinicSlug\": \"{{clinicSlug}}\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{baseUrl}}/patient/auth/register",
                  "host": ["{{baseUrl}}"],
                  "path": ["patient", "auth", "register"]
                },
                "description": "Crea o actualiza la cuenta móvil del paciente y envía un OTP de 6 dígitos por SMS (o correo si no hay celular).\n\nNo requiere API Key.\n\n**Body**\n| Campo | Tipo | Requerido | Descripción |\n|-------|------|-----------|-------------|\n| cedula | string | ✅ | Cédula del paciente |\n| phone | string | — | Celular (ej. `+593987654321`). Si se omite, el OTP va por correo |\n| clinicSlug | string | ✅ | Identificador de la clínica |\n\n**Respuesta 200**\n```json\n{ \"success\": true, \"otpSent\": true, \"channel\": \"sms\", \"maskedTarget\": \"593****21\" }\n```"
              },
              "response": [
                {
                  "name": "200 — OTP enviado por SMS",
                  "status": "OK",
                  "code": 200,
                  "header": [{"key": "Content-Type", "value": "application/json"}],
                  "body": "{\n  \"success\": true,\n  \"otpSent\": true,\n  \"channel\": \"sms\",\n  \"maskedTarget\": \"593****21\"\n}"
                },
                {
                  "name": "200 — OTP enviado por correo",
                  "status": "OK",
                  "code": 200,
                  "header": [{"key": "Content-Type", "value": "application/json"}],
                  "body": "{\n  \"success\": true,\n  \"otpSent\": true,\n  \"channel\": \"email\",\n  \"maskedTarget\": \"pa****@example.com\"\n}"
                },
                {
                  "name": "422 — Sin contacto",
                  "status": "Unprocessable Entity",
                  "code": 422,
                  "header": [{"key": "Content-Type", "value": "application/json"}],
                  "body": "{\n  \"success\": false,\n  \"error\": \"Sin telefono ni correo para el codigo\"\n}"
                }
              ]
            },
            {
              "name": "2. Verify OTP",
              "event": [
                {
                  "listen": "test",
                  "script": {
                    "exec": [
                      "pm.test('Status is 200', () => pm.response.to.have.status(200));",
                      "const body = pm.response.json();",
                      "if (body.verificationToken) {",
                      "  pm.collectionVariables.set('verificationToken', body.verificationToken);",
                      "  console.log('verificationToken guardado');",
                      "}"
                    ],
                    "type": "text/javascript"
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [{"key": "Content-Type", "value": "application/json"}],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"cedula\":     \"1234567890\",\n  \"clinicSlug\": \"{{clinicSlug}}\",\n  \"code\":       \"123456\"\n}",
                  "options": {"raw": {"language": "json"}}
                },
                "url": {
                  "raw": "{{baseUrl}}/patient/auth/verify-otp",
                  "host": ["{{baseUrl}}"],
                  "path": ["patient", "auth", "verify-otp"]
                },
                "description": "Verifica el OTP y retorna un `verificationToken` de 15 min para establecer contraseña.\n\n**Body**\n| Campo | Tipo | Requerido |\n|-------|------|-----------|\n| cedula | string | ✅ |\n| clinicSlug | string | ✅ |\n| code | string | ✅ | Código OTP de 6 dígitos |"
              },
              "response": [
                {
                  "name": "200 — Token de verificación",
                  "status": "OK",
                  "code": 200,
                  "header": [{"key": "Content-Type", "value": "application/json"}],
                  "body": "{\n  \"success\": true,\n  \"verificationToken\": \"eyJhbGciOiJIUzI1NiIs...\",\n  \"needsPassword\": true\n}"
                },
                {
                  "name": "422 — Código inválido",
                  "status": "Unprocessable Entity",
                  "code": 422,
                  "header": [{"key": "Content-Type", "value": "application/json"}],
                  "body": "{\n  \"success\": false,\n  \"error\": \"Codigo invalido o expirado\"\n}"
                }
              ]
            },
            {
              "name": "3. Set Password (primera vez)",
              "event": [
                {
                  "listen": "test",
                  "script": {
                    "exec": [
                      "pm.test('Status is 200', () => pm.response.to.have.status(200));",
                      "const body = pm.response.json();",
                      "if (body.token) {",
                      "  pm.collectionVariables.set('patientToken', body.token);",
                      "  console.log('patientToken guardado');",
                      "}"
                    ],
                    "type": "text/javascript"
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [{"key": "Content-Type", "value": "application/json"}],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"verificationToken\": \"{{verificationToken}}\",\n  \"password\":         \"MiContrasena123\"\n}",
                  "options": {"raw": {"language": "json"}}
                },
                "url": {
                  "raw": "{{baseUrl}}/patient/auth/set-password",
                  "host": ["{{baseUrl}}"],
                  "path": ["patient", "auth", "set-password"]
                },
                "description": "Establece la contraseña del paciente usando el `verificationToken` obtenido en el paso anterior (válido 15 min). Retorna el token de sesión directamente.\n\n**Body**\n| Campo | Tipo | Requerido |\n|-------|------|-----------|\n| verificationToken | string | ✅ | JWT obtenido en verify-otp |\n| password | string | ✅ | Mínimo 6 caracteres |"
              },
              "response": [
                {
                  "name": "200 — Sesión iniciada",
                  "status": "OK",
                  "code": 200,
                  "header": [{"key": "Content-Type", "value": "application/json"}],
                  "body": "{\n  \"success\": true,\n  \"token\": \"eyJhbGciOiJIUzI1NiIs...\",\n  \"patient\": {\n    \"id\": \"uuid\",\n    \"cedula\": \"1234567890\",\n    \"phone\": \"+593987654321\",\n    \"company_slug\": \"mi-clinica\",\n    \"is_verified\": true\n  }\n}"
                },
                {
                  "name": "401 — Token inválido",
                  "status": "Unauthorized",
                  "code": 401,
                  "header": [{"key": "Content-Type", "value": "application/json"}],
                  "body": "{ \"success\": false, \"error\": \"Token de verificacion invalido\" }"
                }
              ]
            },
            {
              "name": "4. Login (cédula o correo + contraseña)",
              "event": [
                {
                  "listen": "test",
                  "script": {
                    "exec": [
                      "pm.test('Status is 200', () => pm.response.to.have.status(200));",
                      "const body = pm.response.json();",
                      "if (body.token) {",
                      "  pm.collectionVariables.set('patientToken', body.token);",
                      "  console.log('patientToken guardado');",
                      "}"
                    ],
                    "type": "text/javascript"
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [{"key": "Content-Type", "value": "application/json"}],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"identifier\": \"1234567890\",\n  \"password\":   \"MiContrasena123\"\n}",
                  "options": {"raw": {"language": "json"}}
                },
                "url": {
                  "raw": "{{baseUrl}}/patient/auth/login",
                  "host": ["{{baseUrl}}"],
                  "path": ["patient", "auth", "login"]
                },
                "description": "Login para pacientes con cuenta ya creada.\n\n⚠️ **Solo requiere cédula (o correo) y contraseña — NO se pide clinicSlug.**\n\n**Body**\n| Campo | Tipo | Requerido | Descripción |\n|-------|------|-----------|-------------|\n| identifier | string | ✅ | Cédula **o** correo electrónico |\n| password | string | ✅ | Contraseña establecida en el registro |"
              },
              "response": [
                {
                  "name": "200 — Sesión iniciada",
                  "status": "OK",
                  "code": 200,
                  "header": [{"key": "Content-Type", "value": "application/json"}],
                  "body": "{\n  \"success\": true,\n  \"token\": \"eyJhbGciOiJIUzI1NiIs...\",\n  \"patient\": {\n    \"id\": \"uuid\",\n    \"cedula\": \"1234567890\",\n    \"email\": \"ana@example.com\",\n    \"company_slug\": \"mi-clinica\",\n    \"is_verified\": true\n  }\n}"
                },
                {
                  "name": "401 — Credenciales incorrectas",
                  "status": "Unauthorized",
                  "code": 401,
                  "header": [{"key": "Content-Type", "value": "application/json"}],
                  "body": "{ \"success\": false, \"error\": \"Credenciales incorrectas\" }"
                }
              ]
            }
          ]
        },
        {
          "name": "Profile",
          "item": [
            {
              "name": "Get My Profile",
              "request": {
                "method": "GET",
                "header": [
                  {
                    "key": "X-API-Key",
                    "value": "{{apiKey}}"
                  },
                  {
                    "key": "X-Patient-Token",
                    "value": "{{patientToken}}"
                  }
                ],
                "url": {
                  "raw": "{{baseUrl}}/patient/me",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "patient",
                    "me"
                  ]
                },
                "description": "Returns the authenticated patient's profile data.\n\n**Headers required**\n- `X-API-Key` — your clinic's API key\n- `X-Patient-Token` — JWT from verify-otp"
              },
              "response": [
                {
                  "name": "200 — Profile",
                  "status": "OK",
                  "code": 200,
                  "header": [
                    {
                      "key": "Content-Type",
                      "value": "application/json"
                    }
                  ],
                  "body": "{\n  \"id\": \"uuid\",\n  \"firstName\": \"Ana\",\n  \"lastName\": \"García\",\n  \"email\": \"ana@example.com\",\n  \"phone\": \"+521234567890\",\n  \"dateOfBirth\": \"1990-05-15\",\n  \"address\": \"Av. Principal 123\"\n}"
                }
              ]
            },
            {
              "name": "Update My Profile",
              "request": {
                "method": "PUT",
                "header": [
                  {
                    "key": "X-API-Key",
                    "value": "{{apiKey}}"
                  },
                  {
                    "key": "X-Patient-Token",
                    "value": "{{patientToken}}"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"phone\": \"+521234567890\",\n  \"address\": \"Nueva dirección 456\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{baseUrl}}/patient/me",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "patient",
                    "me"
                  ]
                },
                "description": "Updates mutable fields on the patient profile (phone, address, etc.).\n\nEmail and name changes require clinic staff approval and cannot be updated here."
              },
              "response": [
                {
                  "name": "200 — Updated",
                  "status": "OK",
                  "code": 200,
                  "header": [
                    {
                      "key": "Content-Type",
                      "value": "application/json"
                    }
                  ],
                  "body": "{\n  \"success\": true,\n  \"patient\": {\n    \"phone\": \"+521234567890\",\n    \"address\": \"Nueva dirección 456\"\n  }\n}"
                }
              ]
            }
          ]
        },
        {
          "name": "Appointments",
          "item": [
            {
              "name": "List My Appointments",
              "request": {
                "method": "GET",
                "header": [
                  {
                    "key": "X-API-Key",
                    "value": "{{apiKey}}"
                  },
                  {
                    "key": "X-Patient-Token",
                    "value": "{{patientToken}}"
                  }
                ],
                "url": {
                  "raw": "{{baseUrl}}/patient/appointments?status=upcoming&limit=10",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "patient",
                    "appointments"
                  ],
                  "query": [
                    {
                      "key": "status",
                      "value": "upcoming",
                      "description": "Filter: upcoming | past | all"
                    },
                    {
                      "key": "limit",
                      "value": "10",
                      "description": "Max records (default 20)"
                    }
                  ]
                },
                "description": "Returns a list of the authenticated patient's appointments.\n\n**Query params**\n| Param | Description |\n|-------|-------------|\n| status | `upcoming` \\| `past` \\| `all` (default: `all`) |\n| limit | Max records to return (default 20) |"
              },
              "response": [
                {
                  "name": "200 — Appointment List",
                  "status": "OK",
                  "code": 200,
                  "header": [
                    {
                      "key": "Content-Type",
                      "value": "application/json"
                    }
                  ],
                  "body": "{\n  \"appointments\": [\n    {\n      \"id\": \"uuid\",\n      \"date\": \"2026-04-10\",\n      \"time\": \"10:00\",\n      \"dentist\": \"Dr. Martínez\",\n      \"status\": \"scheduled\",\n      \"reason\": \"Limpieza dental\"\n    }\n  ]\n}"
                }
              ]
            },
            {
              "name": "Book Appointment",
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "X-API-Key",
                    "value": "{{apiKey}}"
                  },
                  {
                    "key": "X-Patient-Token",
                    "value": "{{patientToken}}"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"dentistId\": \"uuid-del-dentista\",\n  \"date\": \"2026-04-10\",\n  \"time\": \"10:00\",\n  \"reason\": \"Limpieza dental\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{baseUrl}}/patient/appointments",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "patient",
                    "appointments"
                  ]
                },
                "description": "Books a new appointment for the authenticated patient.\n\n**Body**\n| Field | Type | Required | Description |\n|-------|------|----------|-------------|\n| dentistId | UUID | ✅ | ID of the treating dentist |\n| date | YYYY-MM-DD | ✅ | Appointment date |\n| time | HH:MM | ✅ | Appointment time (24h) |\n| reason | string | ✅ | Reason for visit |"
              },
              "response": [
                {
                  "name": "201 — Booked",
                  "status": "Created",
                  "code": 201,
                  "header": [
                    {
                      "key": "Content-Type",
                      "value": "application/json"
                    }
                  ],
                  "body": "{\n  \"success\": true,\n  \"appointment\": {\n    \"id\": \"uuid\",\n    \"date\": \"2026-04-10\",\n    \"time\": \"10:00\",\n    \"status\": \"scheduled\"\n  }\n}"
                }
              ]
            }
          ]
        },
        {
          "name": "Treatments",
          "item": [
            {
              "name": "List My Treatments",
              "request": {
                "method": "GET",
                "header": [
                  {
                    "key": "X-API-Key",
                    "value": "{{apiKey}}"
                  },
                  {
                    "key": "X-Patient-Token",
                    "value": "{{patientToken}}"
                  }
                ],
                "url": {
                  "raw": "{{baseUrl}}/patient/treatments",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "patient",
                    "treatments"
                  ]
                },
                "description": "Returns all treatments associated with the authenticated patient."
              },
              "response": [
                {
                  "name": "200 — Treatment List",
                  "status": "OK",
                  "code": 200,
                  "header": [
                    {
                      "key": "Content-Type",
                      "value": "application/json"
                    }
                  ],
                  "body": "{\n  \"treatments\": [\n    {\n      \"id\": \"uuid\",\n      \"name\": \"Extracción simple\",\n      \"status\": \"completed\",\n      \"date\": \"2026-03-15\",\n      \"price\": 800.00\n    }\n  ]\n}"
                }
              ]
            }
          ]
        },
        {
          "name": "Invoices",
          "item": [
            {
              "name": "List My Invoices",
              "request": {
                "method": "GET",
                "header": [
                  {
                    "key": "X-API-Key",
                    "value": "{{apiKey}}"
                  },
                  {
                    "key": "X-Patient-Token",
                    "value": "{{patientToken}}"
                  }
                ],
                "url": {
                  "raw": "{{baseUrl}}/patient/invoices",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "patient",
                    "invoices"
                  ]
                },
                "description": "Returns all invoices associated with the authenticated patient."
              },
              "response": [
                {
                  "name": "200 — Invoice List",
                  "status": "OK",
                  "code": 200,
                  "header": [
                    {
                      "key": "Content-Type",
                      "value": "application/json"
                    }
                  ],
                  "body": "{\n  \"invoices\": [\n    {\n      \"id\": \"uuid\",\n      \"number\": \"INV-0042\",\n      \"date\": \"2026-03-15\",\n      \"total\": 800.00,\n      \"status\": \"paid\"\n    }\n  ]\n}"
                }
              ]
            }
          ]
        }
      ]
    },
    {
      "name": "Connect API",
      "description": "Third-party integration API for clinic management systems.\n\nAll endpoints require `X-API-Key` header. Data is scoped to the clinic that owns the API key.",
      "item": [
        {
          "name": "Patients",
          "item": [
            {
              "name": "List Patients",
              "request": {
                "method": "GET",
                "header": [
                  {
                    "key": "X-API-Key",
                    "value": "{{apiKey}}"
                  }
                ],
                "url": {
                  "raw": "{{baseUrl}}/connect/v1/patients?page=1&limit=20&search=",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "connect",
                    "v1",
                    "patients"
                  ],
                  "query": [
                    {
                      "key": "page",
                      "value": "1",
                      "description": "Page number (1-indexed)"
                    },
                    {
                      "key": "limit",
                      "value": "20",
                      "description": "Records per page (max 100)"
                    },
                    {
                      "key": "search",
                      "value": "",
                      "description": "Search by name or email"
                    }
                  ]
                },
                "description": "Returns a paginated list of patients belonging to the API key's clinic.\n\n**Query params**\n| Param | Default | Description |\n|-------|---------|-------------|\n| page | 1 | Page number |\n| limit | 20 | Records per page (max 100) |\n| search | — | Filter by name or email |"
              },
              "response": [
                {
                  "name": "200 — Patient List",
                  "status": "OK",
                  "code": 200,
                  "header": [
                    {
                      "key": "Content-Type",
                      "value": "application/json"
                    }
                  ],
                  "body": "{\n  \"patients\": [\n    {\n      \"id\": \"uuid\",\n      \"firstName\": \"Ana\",\n      \"lastName\": \"García\",\n      \"email\": \"ana@example.com\",\n      \"phone\": \"+521234567890\"\n    }\n  ],\n  \"pagination\": {\n    \"page\": 1,\n    \"limit\": 20,\n    \"total\": 150\n  }\n}"
                }
              ]
            },
            {
              "name": "Get Patient by ID",
              "request": {
                "method": "GET",
                "header": [
                  {
                    "key": "X-API-Key",
                    "value": "{{apiKey}}"
                  }
                ],
                "url": {
                  "raw": "{{baseUrl}}/connect/v1/patients/:id",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "connect",
                    "v1",
                    "patients",
                    ":id"
                  ],
                  "variable": [
                    {
                      "key": "id",
                      "value": "uuid-del-paciente"
                    }
                  ]
                },
                "description": "Returns a single patient by UUID. Returns 404 if the patient does not belong to the API key's clinic."
              },
              "response": [
                {
                  "name": "200 — Patient",
                  "status": "OK",
                  "code": 200,
                  "header": [
                    {
                      "key": "Content-Type",
                      "value": "application/json"
                    }
                  ],
                  "body": "{\n  \"id\": \"uuid\",\n  \"firstName\": \"Ana\",\n  \"lastName\": \"García\",\n  \"email\": \"ana@example.com\",\n  \"phone\": \"+521234567890\",\n  \"dateOfBirth\": \"1990-05-15\"\n}"
                },
                {
                  "name": "404 — Not Found",
                  "status": "Not Found",
                  "code": 404,
                  "header": [
                    {
                      "key": "Content-Type",
                      "value": "application/json"
                    }
                  ],
                  "body": "{ \"success\": false, \"error\": \"Patient not found\" }"
                }
              ]
            },
            {
              "name": "Create Patient",
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "X-API-Key",
                    "value": "{{apiKey}}"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"firstName\": \"Ana\",\n  \"lastName\": \"García\",\n  \"email\": \"ana@example.com\",\n  \"phone\": \"+521234567890\",\n  \"dateOfBirth\": \"1990-05-15\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{baseUrl}}/connect/v1/patients",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "connect",
                    "v1",
                    "patients"
                  ]
                },
                "description": "Creates a new patient record in the clinic's database.\n\n**Body**\n| Field | Type | Required | Description |\n|-------|------|----------|-------------|\n| firstName | string | ✅ | Patient first name |\n| lastName | string | ✅ | Patient last name |\n| email | string | ✅ | Unique patient email |\n| phone | string | — | Phone number |\n| dateOfBirth | YYYY-MM-DD | — | Date of birth |"
              },
              "response": [
                {
                  "name": "201 — Created",
                  "status": "Created",
                  "code": 201,
                  "header": [
                    {
                      "key": "Content-Type",
                      "value": "application/json"
                    }
                  ],
                  "body": "{\n  \"success\": true,\n  \"patient\": {\n    \"id\": \"new-uuid\",\n    \"firstName\": \"Ana\",\n    \"lastName\": \"García\",\n    \"email\": \"ana@example.com\"\n  }\n}"
                },
                {
                  "name": "409 — Duplicate Email",
                  "status": "Conflict",
                  "code": 409,
                  "header": [
                    {
                      "key": "Content-Type",
                      "value": "application/json"
                    }
                  ],
                  "body": "{ \"success\": false, \"error\": \"A patient with this email already exists\" }"
                }
              ]
            }
          ]
        },
        {
          "name": "Appointments",
          "item": [
            {
              "name": "List Appointments",
              "request": {
                "method": "GET",
                "header": [
                  {
                    "key": "X-API-Key",
                    "value": "{{apiKey}}"
                  }
                ],
                "url": {
                  "raw": "{{baseUrl}}/connect/v1/appointments?from=2026-04-01&to=2026-04-30",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "connect",
                    "v1",
                    "appointments"
                  ],
                  "query": [
                    {
                      "key": "from",
                      "value": "2026-04-01",
                      "description": "Start date (YYYY-MM-DD)"
                    },
                    {
                      "key": "to",
                      "value": "2026-04-30",
                      "description": "End date (YYYY-MM-DD)"
                    },
                    {
                      "key": "dentistId",
                      "value": "",
                      "description": "Filter by dentist UUID"
                    }
                  ]
                },
                "description": "Returns appointments within the given date range, scoped to the API key's clinic.\n\n**Query params**\n| Param | Description |\n|-------|-------------|\n| from | Start date (YYYY-MM-DD) |\n| to | End date (YYYY-MM-DD, default today) |\n| dentistId | Filter by specific dentist |"
              },
              "response": [
                {
                  "name": "200 — Appointment List",
                  "status": "OK",
                  "code": 200,
                  "header": [
                    {
                      "key": "Content-Type",
                      "value": "application/json"
                    }
                  ],
                  "body": "{\n  \"appointments\": [\n    {\n      \"id\": \"uuid\",\n      \"patientId\": \"uuid\",\n      \"patientName\": \"Ana García\",\n      \"dentistId\": \"uuid\",\n      \"dentistName\": \"Dr. Martínez\",\n      \"date\": \"2026-04-10\",\n      \"time\": \"10:00\",\n      \"status\": \"scheduled\"\n    }\n  ]\n}"
                }
              ]
            },
            {
              "name": "Create Appointment",
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "X-API-Key",
                    "value": "{{apiKey}}"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"patientId\": \"uuid-del-paciente\",\n  \"dentistId\": \"uuid-del-dentista\",\n  \"date\": \"2026-04-10\",\n  \"time\": \"10:00\",\n  \"reason\": \"Revisión general\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{baseUrl}}/connect/v1/appointments",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "connect",
                    "v1",
                    "appointments"
                  ]
                },
                "description": "Creates a new appointment. Both `patientId` and `dentistId` must belong to the API key's clinic.\n\n**Body**\n| Field | Type | Required |\n|-------|------|----------|\n| patientId | UUID | ✅ |\n| dentistId | UUID | ✅ |\n| date | YYYY-MM-DD | ✅ |\n| time | HH:MM | ✅ |\n| reason | string | ✅ |"
              },
              "response": [
                {
                  "name": "201 — Created",
                  "status": "Created",
                  "code": 201,
                  "header": [
                    {
                      "key": "Content-Type",
                      "value": "application/json"
                    }
                  ],
                  "body": "{\n  \"success\": true,\n  \"appointment\": {\n    \"id\": \"new-uuid\",\n    \"status\": \"scheduled\"\n  }\n}"
                }
              ]
            }
          ]
        },
        {
          "name": "Webhooks",
          "item": [
            {
              "name": "List Webhooks",
              "request": {
                "method": "GET",
                "header": [
                  {
                    "key": "X-API-Key",
                    "value": "{{apiKey}}"
                  }
                ],
                "url": {
                  "raw": "{{baseUrl}}/connect/v1/webhooks",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "connect",
                    "v1",
                    "webhooks"
                  ]
                },
                "description": "Lists all registered webhook endpoints for the API key's account."
              },
              "response": [
                {
                  "name": "200 — Webhook List",
                  "status": "OK",
                  "code": 200,
                  "header": [
                    {
                      "key": "Content-Type",
                      "value": "application/json"
                    }
                  ],
                  "body": "{\n  \"webhooks\": [\n    {\n      \"id\": \"uuid\",\n      \"url\": \"https://myapp.com/hooks/dentisoft\",\n      \"events\": [\"appointment.created\", \"appointment.updated\"],\n      \"isActive\": true\n    }\n  ]\n}"
                }
              ]
            },
            {
              "name": "Register Webhook",
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "X-API-Key",
                    "value": "{{apiKey}}"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"url\": \"https://myapp.com/hooks/dentisoft\",\n  \"events\": [\n    \"appointment.created\",\n    \"appointment.updated\",\n    \"appointment.cancelled\",\n    \"patient.created\",\n    \"invoice.paid\"\n  ]\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{baseUrl}}/connect/v1/webhooks",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "connect",
                    "v1",
                    "webhooks"
                  ]
                },
                "description": "Registers a new webhook endpoint to receive event notifications.\n\n**Available events**\n- `appointment.created` — New appointment booked\n- `appointment.updated` — Appointment rescheduled or status changed\n- `appointment.cancelled` — Appointment cancelled\n- `patient.created` — New patient registered\n- `invoice.paid` — Invoice marked as paid\n\n**Body**\n| Field | Type | Required |\n|-------|------|----------|\n| url | HTTPS URL | ✅ |\n| events | string[] | ✅ |"
              },
              "response": [
                {
                  "name": "201 — Registered",
                  "status": "Created",
                  "code": 201,
                  "header": [
                    {
                      "key": "Content-Type",
                      "value": "application/json"
                    }
                  ],
                  "body": "{\n  \"success\": true,\n  \"webhook\": {\n    \"id\": \"new-uuid\",\n    \"url\": \"https://myapp.com/hooks/dentisoft\",\n    \"events\": [\"appointment.created\", \"appointment.updated\", \"appointment.cancelled\", \"patient.created\", \"invoice.paid\"],\n    \"secret\": \"whsec_abc123xyz\"\n  }\n}"
                }
              ]
            }
          ]
        },
        {
          "name": "Invoices",
          "item": [
            {
              "name": "List Invoices",
              "request": {
                "method": "GET",
                "header": [
                  {
                    "key": "X-API-Key",
                    "value": "{{apiKey}}"
                  }
                ],
                "url": {
                  "raw": "{{baseUrl}}/connect/v1/invoices",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "connect",
                    "v1",
                    "invoices"
                  ],
                  "query": [
                    {
                      "key": "status",
                      "value": "",
                      "disabled": true
                    },
                    {
                      "key": "patientId",
                      "value": "",
                      "disabled": true
                    }
                  ]
                },
                "description": "Returns all invoices for the clinic. Optional filters: `status` (pending | paid | partial | overdue | cancelled) and `patientId`."
              },
              "response": [
                {
                  "name": "200 – Invoice List",
                  "status": "OK",
                  "code": 200,
                  "header": [
                    {
                      "key": "Content-Type",
                      "value": "application/json"
                    }
                  ],
                  "body": "{\n  \"success\": true,\n  \"data\": [\n    {\n      \"id\": \"uuid\",\n      \"patient_id\": \"uuid\",\n      \"total_amount\": 350.00,\n      \"status\": \"pending\",\n      \"due_date\": \"2025-08-01\"\n    }\n  ]\n}"
                }
              ]
            },
            {
              "name": "List Overdue Invoices",
              "request": {
                "method": "GET",
                "header": [
                  {
                    "key": "X-API-Key",
                    "value": "{{apiKey}}"
                  }
                ],
                "url": {
                  "raw": "{{baseUrl}}/connect/v1/invoices/overdue",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "connect",
                    "v1",
                    "invoices",
                    "overdue"
                  ]
                },
                "description": "Returns all unpaid invoices whose due date has passed."
              },
              "response": []
            },
            {
              "name": "Get Invoice",
              "request": {
                "method": "GET",
                "header": [
                  {
                    "key": "X-API-Key",
                    "value": "{{apiKey}}"
                  }
                ],
                "url": {
                  "raw": "{{baseUrl}}/connect/v1/invoices/:id",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "connect",
                    "v1",
                    "invoices",
                    ":id"
                  ],
                  "variable": [
                    {
                      "key": "id",
                      "value": "{{invoiceId}}"
                    }
                  ]
                },
                "description": "Returns a single invoice with its line items."
              },
              "response": []
            },
            {
              "name": "Update Invoice Status",
              "request": {
                "method": "PATCH",
                "header": [
                  {
                    "key": "X-API-Key",
                    "value": "{{apiKey}}"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"status\": \"paid\",\n  \"paidAmount\": 350.00\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{baseUrl}}/connect/v1/invoices/:id/status",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "connect",
                    "v1",
                    "invoices",
                    ":id",
                    "status"
                  ],
                  "variable": [
                    {
                      "key": "id",
                      "value": "{{invoiceId}}"
                    }
                  ]
                },
                "description": "Updates the payment status of an invoice.\n\n**status values**: `pending` | `paid` | `partial` | `overdue` | `cancelled`"
              },
              "response": []
            }
          ]
        },
        {
          "name": "Treatments",
          "item": [
            {
              "name": "List Treatments",
              "request": {
                "method": "GET",
                "header": [
                  {
                    "key": "X-API-Key",
                    "value": "{{apiKey}}"
                  }
                ],
                "url": {
                  "raw": "{{baseUrl}}/connect/v1/treatments",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "connect",
                    "v1",
                    "treatments"
                  ],
                  "query": [
                    {
                      "key": "patientId",
                      "value": "",
                      "disabled": true
                    },
                    {
                      "key": "status",
                      "value": "",
                      "disabled": true
                    }
                  ]
                },
                "description": "Returns treatments for the clinic. Filter by `patientId` or `status` (pending | in_progress | completed | cancelled)."
              },
              "response": []
            },
            {
              "name": "Get Treatment",
              "request": {
                "method": "GET",
                "header": [
                  {
                    "key": "X-API-Key",
                    "value": "{{apiKey}}"
                  }
                ],
                "url": {
                  "raw": "{{baseUrl}}/connect/v1/treatments/:id",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "connect",
                    "v1",
                    "treatments",
                    ":id"
                  ],
                  "variable": [
                    {
                      "key": "id",
                      "value": "{{treatmentId}}"
                    }
                  ]
                },
                "description": "Returns a single treatment record."
              },
              "response": []
            },
            {
              "name": "Update Treatment Status",
              "request": {
                "method": "PATCH",
                "header": [
                  {
                    "key": "X-API-Key",
                    "value": "{{apiKey}}"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"status\": \"completed\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{baseUrl}}/connect/v1/treatments/:id/status",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "connect",
                    "v1",
                    "treatments",
                    ":id",
                    "status"
                  ],
                  "variable": [
                    {
                      "key": "id",
                      "value": "{{treatmentId}}"
                    }
                  ]
                },
                "description": "Updates the status of a treatment.\n\n**status values**: `pending` | `in_progress` | `completed` | `cancelled`"
              },
              "response": []
            }
          ]
        },
        {
          "name": "Inventory",
          "item": [
            {
              "name": "List Inventory",
              "request": {
                "method": "GET",
                "header": [
                  {
                    "key": "X-API-Key",
                    "value": "{{apiKey}}"
                  }
                ],
                "url": {
                  "raw": "{{baseUrl}}/connect/v1/inventory",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "connect",
                    "v1",
                    "inventory"
                  ],
                  "query": [
                    {
                      "key": "search",
                      "value": "",
                      "disabled": true
                    },
                    {
                      "key": "category",
                      "value": "",
                      "disabled": true
                    }
                  ]
                },
                "description": "Returns all inventory items. Optionally filter by `search` (name) or `category`."
              },
              "response": []
            },
            {
              "name": "List Low-Stock Items",
              "request": {
                "method": "GET",
                "header": [
                  {
                    "key": "X-API-Key",
                    "value": "{{apiKey}}"
                  }
                ],
                "url": {
                  "raw": "{{baseUrl}}/connect/v1/inventory/low-stock",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "connect",
                    "v1",
                    "inventory",
                    "low-stock"
                  ]
                },
                "description": "Returns items whose current quantity is at or below their minimum stock threshold."
              },
              "response": []
            },
            {
              "name": "Record Inventory Movement",
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "X-API-Key",
                    "value": "{{apiKey}}"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"type\": \"out\",\n  \"quantity\": 5,\n  \"reason\": \"Used in procedure\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{baseUrl}}/connect/v1/inventory/:id/movement",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "connect",
                    "v1",
                    "inventory",
                    ":id",
                    "movement"
                  ],
                  "variable": [
                    {
                      "key": "id",
                      "value": "{{itemId}}"
                    }
                  ]
                },
                "description": "Records a stock movement (entry or withdrawal) for an inventory item.\n\n**type**: `in` (stock received) | `out` (stock consumed)"
              },
              "response": []
            }
          ]
        },
        {
          "name": "Dentists",
          "item": [
            {
              "name": "List Dentists",
              "request": {
                "method": "GET",
                "header": [
                  {
                    "key": "X-API-Key",
                    "value": "{{apiKey}}"
                  }
                ],
                "url": {
                  "raw": "{{baseUrl}}/connect/v1/dentists",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "connect",
                    "v1",
                    "dentists"
                  ]
                },
                "description": "Returns all active dentists (users with appointment access) for the clinic."
              },
              "response": []
            },
            {
              "name": "Get Dentist Availability",
              "request": {
                "method": "GET",
                "header": [
                  {
                    "key": "X-API-Key",
                    "value": "{{apiKey}}"
                  }
                ],
                "url": {
                  "raw": "{{baseUrl}}/connect/v1/dentists/:id/availability?date=2025-07-28",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "connect",
                    "v1",
                    "dentists",
                    ":id",
                    "availability"
                  ],
                  "variable": [
                    {
                      "key": "id",
                      "value": "{{dentistId}}"
                    }
                  ],
                  "query": [
                    {
                      "key": "date",
                      "value": "2025-07-28"
                    }
                  ]
                },
                "description": "Returns the booked appointment slots for a dentist on a given date (YYYY-MM-DD)."
              },
              "response": []
            }
          ]
        },
        {
          "name": "Stats",
          "item": [
            {
              "name": "Stats Overview",
              "request": {
                "method": "GET",
                "header": [
                  {
                    "key": "X-API-Key",
                    "value": "{{apiKey}}"
                  }
                ],
                "url": {
                  "raw": "{{baseUrl}}/connect/v1/stats/overview",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "connect",
                    "v1",
                    "stats",
                    "overview"
                  ]
                },
                "description": "Returns a summary of clinic statistics: total active patients, appointment counts (today / this month / pending), and revenue (this month / all time)."
              },
              "response": [
                {
                  "name": "200 – Overview",
                  "status": "OK",
                  "code": 200,
                  "header": [
                    {
                      "key": "Content-Type",
                      "value": "application/json"
                    }
                  ],
                  "body": "{\n  \"success\": true,\n  \"data\": {\n    \"patients\": { \"total\": 120, \"active\": 95 },\n    \"appointments\": { \"today\": 8, \"this_month\": 52, \"pending\": 14 },\n    \"revenue\": { \"revenue_this_month\": 12500.00, \"revenue_all_time\": 98400.00 }\n  }\n}"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}