{
  "openapi": "3.0.3",
  "info": {
    "title": "AIHR BD API",
    "version": "1.1.0",
    "description": "Public REST API and internal Edge Functions for the AIHR BD multi-tenant HRM platform.\n\n## Authentication\n- **ApiKeyAuth** (`x-api-key` header) — for the public REST API (`/hrm-public-api/*`). Generate keys from the in-app **Integrations** page.\n- **BearerAuth** (Supabase user JWT) — for admin / HR / employee actions; obtained via Supabase Auth login.\n- **ServiceSecretAuth** (`x-service-secret` header) — used by database triggers to call internal email senders.\n\n## Rate limits & quotas\n- Public REST endpoints accept `limit` up to **500** rows per call.\n- Default `limit` is **100** when not supplied.\n\n## Changelog\n- **1.1.0** — Internal device sync now supports Dahua (HTTP/CGI) and Tipsoi (ZKTeco TCP) in addition to Hikvision and ZKTeco. Bank transfer added as a payment method on subscription billing.\n- **1.0.0** — Initial public release: employees, attendance and payroll endpoints.\n",
    "contact": {
      "name": "AIHR BD Support",
      "url": "https://aihrbd.com/contact",
      "email": "support@aihrbd.com"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://aihrbd.com/terms"
    },
    "termsOfService": "https://aihrbd.com/terms"
  },
  "externalDocs": {
    "description": "AIHR BD documentation",
    "url": "https://aihrbd.com/docs"
  },
  "servers": [
    {
      "url": "https://uqxkczspnnhkyiljmivz.supabase.co/functions/v1",
      "description": "Production — Edge Functions"
    }
  ],
  "tags": [
    { "name": "Public API", "description": "Read-only REST API for third-party integrations (API key required)." },
    { "name": "Email", "description": "Transactional email delivery." },
    { "name": "Devices", "description": "Attendance device synchronization (Hikvision / ZKTeco)." },
    { "name": "Admin", "description": "Privileged operations — admin/HR/super_admin only." },
    { "name": "Integrations", "description": "Third-party integrations and webhooks." },
    { "name": "Public", "description": "Endpoints reachable without authentication." }
  ],
  "paths": {
    "/hrm-public-api": {
      "get": {
        "tags": ["Public API"],
        "summary": "API root / discovery",
        "description": "Returns API metadata and the list of available endpoints.",
        "security": [{ "ApiKeyAuth": [] }],
        "responses": {
          "200": {
            "description": "API metadata",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": { "type": "string", "example": "AIHR BD Public API" },
                    "version": { "type": "string", "example": "v1" },
                    "endpoints": { "type": "array", "items": { "type": "string" } }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/hrm-public-api/employees": {
      "get": {
        "tags": ["Public API"],
        "summary": "List employees",
        "description": "Returns active and former employees of the company that owns the API key.",
        "parameters": [{ "$ref": "#/components/parameters/Limit" }],
        "security": [{ "ApiKeyAuth": [] }],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": { "type": "array", "items": { "$ref": "#/components/schemas/Employee" } }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "8f1a2b30-c8e2-4a4d-9b97-2e1c1f3aab01",
                      "employee_id": "EMP-0001",
                      "name": "Rakib Hasan",
                      "email": "rakib@example.com",
                      "phone": "+8801712345678",
                      "designation": "Software Engineer",
                      "department": "Engineering",
                      "gross_salary": 65000,
                      "basic_salary": 39000,
                      "join_date": "2024-03-15",
                      "status": "Active"
                    },
                    {
                      "id": "1d6c4f10-2bc9-44a7-8a3a-9b0aa12c4efa",
                      "employee_id": "EMP-0002",
                      "name": "Sumaiya Akter",
                      "email": "sumaiya@example.com",
                      "phone": "+8801911223344",
                      "designation": "HR Manager",
                      "department": "Human Resources",
                      "gross_salary": 80000,
                      "basic_salary": 48000,
                      "join_date": "2023-08-01",
                      "status": "Active"
                    }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/hrm-public-api/attendance": {
      "get": {
        "tags": ["Public API"],
        "summary": "List attendance records",
        "description": "Returns attendance entries within an optional date range.",
        "parameters": [
          { "name": "from", "in": "query", "description": "Start date (inclusive, YYYY-MM-DD).", "schema": { "type": "string", "format": "date" } },
          { "name": "to", "in": "query", "description": "End date (inclusive, YYYY-MM-DD).", "schema": { "type": "string", "format": "date" } },
          { "$ref": "#/components/parameters/Limit" }
        ],
        "security": [{ "ApiKeyAuth": [] }],
        "responses": {
          "200": {
            "description": "Attendance rows",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": { "type": "array", "items": { "$ref": "#/components/schemas/Attendance" } }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "a1c2-...",
                      "employee_id": "8f1a2b30-c8e2-4a4d-9b97-2e1c1f3aab01",
                      "date": "2026-01-15",
                      "check_in": "09:01:23",
                      "check_out": "18:05:11",
                      "status": "Present"
                    },
                    {
                      "id": "b2d3-...",
                      "employee_id": "8f1a2b30-c8e2-4a4d-9b97-2e1c1f3aab01",
                      "date": "2026-01-16",
                      "check_in": "09:35:02",
                      "check_out": "18:02:44",
                      "status": "Late"
                    }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/hrm-public-api/payroll": {
      "get": {
        "tags": ["Public API"],
        "summary": "List payroll runs",
        "description": "Returns payroll rows filterable by month and year.",
        "parameters": [
          { "name": "month", "in": "query", "description": "Month 1–12.", "schema": { "type": "integer", "minimum": 1, "maximum": 12 } },
          { "name": "year", "in": "query", "description": "4-digit year.", "schema": { "type": "integer", "minimum": 2000 } },
          { "$ref": "#/components/parameters/Limit" }
        ],
        "security": [{ "ApiKeyAuth": [] }],
        "responses": {
          "200": {
            "description": "Payroll rows",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": { "type": "array", "items": { "$ref": "#/components/schemas/Payroll" } }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "p1q2-...",
                      "employee_id": "8f1a2b30-c8e2-4a4d-9b97-2e1c1f3aab01",
                      "month": 1,
                      "year": 2026,
                      "basic_salary": 39000,
                      "net_salary": 61450,
                      "tax": 1500,
                      "provident_fund": 2050,
                      "status": "paid",
                      "paid_at": "2026-02-02T10:15:32Z"
                    }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/send-email": {
      "post": {
        "tags": ["Email"],
        "summary": "Send transactional email",
        "description": "Sends an HTML email via Resend. Caller must authenticate either with `x-service-secret` (DB triggers) **or** an admin/HR/super_admin Bearer JWT.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/SendEmailRequest" },
              "example": {
                "to": "ceo@example.com",
                "subject": "Monthly payroll ready",
                "html": "<p>Hello,</p><p>The January 2026 payroll has been processed.</p>",
                "replyTo": "noreply@aihrbd.com"
              }
            }
          }
        },
        "security": [
          { "ServiceSecretAuth": [] },
          { "BearerAuth": [] }
        ],
        "responses": {
          "200": {
            "description": "Email accepted by provider",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "id": { "type": "string", "description": "Resend message id" }
                  }
                },
                "example": { "success": true, "id": "re_8h3k2m9n4p" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/admin-manage-users": {
      "post": {
        "tags": ["Admin"],
        "summary": "Manage users (admin)",
        "description": "Create / invite / disable users. Requires admin or super_admin role.",
        "security": [{ "BearerAuth": [] }],
        "responses": {
          "200": { "description": "OK" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/sync-zkteco": {
      "post": {
        "tags": ["Devices"],
        "summary": "Sync a ZKTeco device",
        "security": [{ "BearerAuth": [] }],
        "responses": {
          "200": { "description": "Synced" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/sync-hikvision": {
      "post": {
        "tags": ["Devices"],
        "summary": "Sync a Hikvision device",
        "security": [{ "BearerAuth": [] }],
        "responses": {
          "200": { "description": "Synced" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/sync-all-devices": {
      "post": {
        "tags": ["Devices"],
        "summary": "Sync every device for a company",
        "security": [{ "BearerAuth": [] }],
        "responses": {
          "200": { "description": "Synced" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/quickbooks-sync": {
      "post": {
        "tags": ["Integrations"],
        "summary": "Sync payroll → QuickBooks",
        "security": [{ "BearerAuth": [] }],
        "responses": {
          "200": { "description": "Synced" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/leave-management": {
      "post": {
        "tags": ["Admin"],
        "summary": "Approve / reject leave",
        "security": [{ "BearerAuth": [] }],
        "responses": {
          "200": { "description": "OK" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/api-keys-manage": {
      "post": {
        "tags": ["Integrations"],
        "summary": "Create or revoke API keys",
        "security": [{ "BearerAuth": [] }],
        "responses": {
          "200": { "description": "OK" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/contact-submit": {
      "post": {
        "tags": ["Public"],
        "summary": "Submit a contact-us form",
        "description": "Public endpoint — no authentication required.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["name", "email", "message"],
                "properties": {
                  "name": { "type": "string" },
                  "email": { "type": "string", "format": "email" },
                  "phone": { "type": "string" },
                  "company": { "type": "string" },
                  "message": { "type": "string" }
                }
              },
              "example": {
                "name": "Anika Rahman",
                "email": "anika@acme.com.bd",
                "phone": "+8801712345678",
                "company": "Acme Ltd.",
                "message": "We have 120 employees and want a demo of payroll + bKash disbursement."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Submitted",
            "content": {
              "application/json": {
                "example": { "success": true }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" }
        }
      }
    },
    "/webhook-dispatch": {
      "post": {
        "tags": ["Integrations"],
        "summary": "Dispatch outgoing webhooks",
        "description": "Internal endpoint that fans out an event to registered webhook endpoints.",
        "security": [{ "BearerAuth": [] }],
        "responses": {
          "200": { "description": "Dispatched" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "Generate from in-app Integrations → API Keys."
      },
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Supabase user JWT."
      },
      "ServiceSecretAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-service-secret",
        "description": "Internal service secret for DB-triggered email."
      }
    },
    "parameters": {
      "Limit": {
        "name": "limit",
        "in": "query",
        "description": "Max rows to return (default 100, max 500).",
        "schema": { "type": "integer", "minimum": 1, "maximum": 500, "default": 100 }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": { "type": "string", "example": "Invalid or revoked API key" }
        }
      },
      "Employee": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "employee_id": { "type": "string", "example": "EMP-0001" },
          "name": { "type": "string" },
          "email": { "type": "string", "format": "email" },
          "phone": { "type": "string" },
          "designation": { "type": "string" },
          "department": { "type": "string" },
          "gross_salary": { "type": "number", "format": "double" },
          "basic_salary": { "type": "number", "format": "double" },
          "join_date": { "type": "string", "format": "date" },
          "status": { "type": "string", "enum": ["Active", "Inactive", "Terminated"] }
        }
      },
      "Attendance": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "employee_id": { "type": "string", "format": "uuid" },
          "date": { "type": "string", "format": "date" },
          "check_in": { "type": "string", "example": "09:01:23" },
          "check_out": { "type": "string", "example": "18:05:11" },
          "status": { "type": "string", "enum": ["Present", "Absent", "Late", "Half Day", "Leave"] }
        }
      },
      "Payroll": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "employee_id": { "type": "string", "format": "uuid" },
          "month": { "type": "integer", "minimum": 1, "maximum": 12 },
          "year": { "type": "integer" },
          "basic_salary": { "type": "number", "format": "double" },
          "net_salary": { "type": "number", "format": "double" },
          "tax": { "type": "number", "format": "double" },
          "provident_fund": { "type": "number", "format": "double" },
          "status": { "type": "string", "enum": ["pending", "processed", "paid"] },
          "paid_at": { "type": "string", "format": "date-time", "nullable": true }
        }
      },
      "SendEmailRequest": {
        "type": "object",
        "required": ["to", "subject", "html"],
        "properties": {
          "to": {
            "oneOf": [
              { "type": "string", "format": "email" },
              { "type": "array", "items": { "type": "string", "format": "email" } }
            ]
          },
          "subject": { "type": "string" },
          "html": { "type": "string" },
          "replyTo": { "type": "string", "format": "email" },
          "from": { "type": "string", "example": "AIHR BD <onboarding@resend.dev>" }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Validation error",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": { "error": "Field 'email' is required" }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing or invalid credentials",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": { "error": "Invalid or revoked API key" }
          }
        }
      },
      "Forbidden": {
        "description": "Insufficient role / scope",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": { "error": "API key lacks write scope" }
          }
        }
      },
      "ServerError": {
        "description": "Internal server error",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": { "error": "Internal server error" }
          }
        }
      }
    }
  }
}
