openapi: 3.1.0
info:
  title: VipraGo API
  version: 1.0.0-beta
  summary: REST API for VipraGo, the AI Workflow Operating System (AI-WOS) by Vipra Software.
  description: |
    The VipraGo API exposes the platform's workforce primitives — employees, attendance,
    leave, payroll previews, and skill search — so your systems and AI agents can read
    state and submit requests that flow through VipraGo's policy and approval engine.

    **Status: public beta.** The surface below is read-first by design: write operations
    create *requests* that enter the same approval chains a human action would — nothing
    bypasses policy. Request an API key from your Control Center (Admin → API Keys) or
    email support@viprasoftware.com.

    ### Principles
    - **Tenant-scoped**: every key is bound to one organization; data never crosses tenants.
    - **Policy-governed writes**: POST endpoints submit requests; VipraGo's agents and
      approval chains decide outcomes. Responses return a request id you can poll.
    - **Audited**: every API call lands in the immutable audit log (HMAC row-sealed),
      same as UI actions.
    - **Versioned**: breaking changes only in new major versions; deprecations announced
      in the changelog (https://viprago.com/changelog) at least 90 days ahead.

    ### Rate limits
    600 requests/minute per key (burst 100). `429` responses include `Retry-After`.

    Also available: the open-source MCP server for AI agents —
    https://github.com/viprasoftware/viprago-mcp-server
  termsOfService: https://viprago.com/terms
  contact:
    name: VipraGo API Support
    email: support@viprasoftware.com
    url: https://viprago.com/docs/api-reference
  license:
    name: API client code samples MIT-licensed
    identifier: MIT
externalDocs:
  description: Human-readable API guide & webhooks documentation
  url: https://viprago.com/docs/api-and-webhooks
servers:
  - url: https://api.viprago.com/v1
    description: Production (beta access)
tags:
  - name: Identity
    description: Who am I, what can this key do
  - name: Employees
    description: Directory and profile reads
  - name: Attendance
    description: Check-in summaries and anomalies
  - name: Leave
    description: Balances and policy-governed leave requests
  - name: Payroll
    description: Run history and dry-run previews (never executes via API in beta)
  - name: Skills
    description: Natural-language skill and availability search
  - name: Webhooks
    description: Event subscriptions (HMAC-signed deliveries)
security:
  - bearerAuth: []
paths:
  /me:
    get:
      tags: [Identity]
      operationId: getMe
      summary: Identify the calling key
      description: Returns the organization, role tier, and scopes bound to this API key. Call it first — it is the cheapest connectivity and auth check.
      responses:
        "200":
          description: Key identity
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Identity" }
        "401": { $ref: "#/components/responses/Unauthorized" }
  /employees:
    get:
      tags: [Employees]
      operationId: listEmployees
      summary: List employees
      description: Paginated directory scoped to the key's permissions. PII fields are masked unless the key has the `pii:read` scope.
      parameters:
        - { name: team, in: query, schema: { type: string }, description: Filter by team slug }
        - { name: location, in: query, schema: { type: string }, description: Filter by office location slug (e.g. bengaluru) }
        - { name: status, in: query, schema: { type: string, enum: [active, onboarding, offboarded] } }
        - { $ref: "#/components/parameters/page" }
        - { $ref: "#/components/parameters/perPage" }
      responses:
        "200":
          description: Employee page
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { type: array, items: { $ref: "#/components/schemas/Employee" } }
                  meta: { $ref: "#/components/schemas/PageMeta" }
        "401": { $ref: "#/components/responses/Unauthorized" }
  /employees/{employeeId}:
    get:
      tags: [Employees]
      operationId: getEmployee
      summary: Get one employee
      parameters:
        - { $ref: "#/components/parameters/employeeId" }
      responses:
        "200":
          description: Employee
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Employee" }
        "404": { $ref: "#/components/responses/NotFound" }
  /attendance/summary:
    get:
      tags: [Attendance]
      operationId: getAttendanceSummary
      summary: Attendance summary for a period
      description: Aggregated presence, lateness, overtime, and anomaly counts per employee or team for a date range. The same numbers VipraGo's Attendance Agent acts on.
      parameters:
        - { name: from, in: query, required: true, schema: { type: string, format: date } }
        - { name: to, in: query, required: true, schema: { type: string, format: date } }
        - { name: team, in: query, schema: { type: string } }
        - { name: employeeId, in: query, schema: { type: string } }
      responses:
        "200":
          description: Summary rows
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { type: array, items: { $ref: "#/components/schemas/AttendanceSummaryRow" } }
        "422": { $ref: "#/components/responses/ValidationError" }
  /leave/balances/{employeeId}:
    get:
      tags: [Leave]
      operationId: getLeaveBalance
      summary: Leave balances for an employee
      description: Current balances per leave type, including accruals, carry-forward, and pending deductions.
      parameters:
        - { $ref: "#/components/parameters/employeeId" }
      responses:
        "200":
          description: Balances
          content:
            application/json:
              schema:
                type: object
                properties:
                  employeeId: { type: string }
                  balances: { type: array, items: { $ref: "#/components/schemas/LeaveBalance" } }
        "404": { $ref: "#/components/responses/NotFound" }
  /leave/requests:
    post:
      tags: [Leave]
      operationId: createLeaveRequest
      summary: Submit a leave request
      description: |
        Creates a leave request that enters VipraGo's policy engine — auto-approved if the
        org's rules allow, otherwise routed down the configured approval chain. The API
        never bypasses policy. Poll the returned request id or subscribe to the
        `leave.decided` webhook.
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/LeaveRequestInput" }
      responses:
        "202":
          description: Request accepted into the approval flow
          content:
            application/json:
              schema: { $ref: "#/components/schemas/RequestTicket" }
        "409":
          description: Conflicts with existing approved leave or insufficient balance
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Error" }
        "422": { $ref: "#/components/responses/ValidationError" }
  /leave/requests/{requestId}:
    get:
      tags: [Leave]
      operationId: getLeaveRequest
      summary: Poll a leave request's status
      parameters:
        - { name: requestId, in: path, required: true, schema: { type: string } }
      responses:
        "200":
          description: Current state
          content:
            application/json:
              schema: { $ref: "#/components/schemas/RequestTicket" }
        "404": { $ref: "#/components/responses/NotFound" }
  /payroll/runs:
    get:
      tags: [Payroll]
      operationId: listPayrollRuns
      summary: List payroll runs
      description: History of payroll runs with status and headline totals. Amount details require the `payroll:read` scope.
      parameters:
        - { name: period, in: query, schema: { type: string, example: "2026-05" }, description: "Month, YYYY-MM" }
        - { $ref: "#/components/parameters/page" }
      responses:
        "200":
          description: Runs
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { type: array, items: { $ref: "#/components/schemas/PayrollRun" } }
                  meta: { $ref: "#/components/schemas/PageMeta" }
  /payroll/preview:
    post:
      tags: [Payroll]
      operationId: previewPayroll
      summary: Dry-run a payroll computation
      description: |
        Computes a full payroll preview — attendance-linked LOP, overtime, statutory
        deductions (PF, ESI, PT, Gratuity) — **without executing anything**. In the beta
        API, payroll execution is deliberately UI-only with human confirmation; the API
        provides previews so agents and systems can reason about outcomes safely.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [period]
              properties:
                period: { type: string, example: "2026-06", description: "Month, YYYY-MM" }
                team: { type: string, description: Optional team scope }
      responses:
        "200":
          description: Preview totals and per-employee lines
          content:
            application/json:
              schema: { $ref: "#/components/schemas/PayrollPreview" }
        "422": { $ref: "#/components/responses/ValidationError" }
  /skills/search:
    get:
      tags: [Skills]
      operationId: searchSkills
      summary: Search people by skill and availability
      description: 'Natural-language skill search over the org graph: e.g. `q="React developers available from July"`. Returns ranked matches with allocation status — the same engine behind VipraBot''s resourcing answers.'
      parameters:
        - { name: q, in: query, required: true, schema: { type: string } }
        - { name: availableFrom, in: query, schema: { type: string, format: date } }
        - { name: limit, in: query, schema: { type: integer, default: 10, maximum: 50 } }
      responses:
        "200":
          description: Ranked matches
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { type: array, items: { $ref: "#/components/schemas/SkillMatch" } }
  /webhooks/subscriptions:
    get:
      tags: [Webhooks]
      operationId: listWebhookSubscriptions
      summary: List webhook subscriptions
      responses:
        "200":
          description: Subscriptions
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { type: array, items: { $ref: "#/components/schemas/WebhookSubscription" } }
    post:
      tags: [Webhooks]
      operationId: createWebhookSubscription
      summary: Subscribe to events
      description: Deliveries are signed with `X-VipraGo-Signature` (HMAC-SHA256 of the raw body using your endpoint secret). Verify before trusting. Retries with exponential backoff for 24h on non-2xx.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [url, events]
              properties:
                url: { type: string, format: uri }
                events:
                  type: array
                  items: { type: string, enum: [attendance.anomaly, leave.decided, payroll.completed, employee.onboarded, employee.offboarded] }
      responses:
        "201":
          description: Created (response includes the signing secret — shown once)
          content:
            application/json:
              schema: { $ref: "#/components/schemas/WebhookSubscription" }
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API key
      description: "Authorization: Bearer vg_live_… (create in Control Center → API Keys)"
  parameters:
    employeeId:
      name: employeeId
      in: path
      required: true
      schema: { type: string }
    page:
      name: page
      in: query
      schema: { type: integer, default: 1, minimum: 1 }
    perPage:
      name: perPage
      in: query
      schema: { type: integer, default: 50, maximum: 200 }
  responses:
    Unauthorized:
      description: Missing or invalid API key
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    NotFound:
      description: Resource not found in this tenant
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    ValidationError:
      description: Request failed validation
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
  schemas:
    Identity:
      type: object
      properties:
        organization: { type: string, example: "acme-industries" }
        keyName: { type: string, example: "hr-integrations" }
        roleTier: { type: string, enum: [viewer, member, manager, admin, owner] }
        scopes: { type: array, items: { type: string }, example: ["employees:read", "leave:write"] }
    Employee:
      type: object
      properties:
        id: { type: string }
        name: { type: string }
        email: { type: string, format: email, description: Masked without pii:read scope }
        team: { type: string }
        location: { type: string, example: "bengaluru" }
        role: { type: string }
        status: { type: string, enum: [active, onboarding, offboarded] }
        joinedOn: { type: string, format: date }
    PageMeta:
      type: object
      properties:
        page: { type: integer }
        perPage: { type: integer }
        total: { type: integer }
    AttendanceSummaryRow:
      type: object
      properties:
        employeeId: { type: string }
        daysPresent: { type: integer }
        daysAbsent: { type: integer }
        lateArrivals: { type: integer }
        overtimeHours: { type: number }
        anomalies: { type: integer, description: IP/geo/device mismatches flagged by the Attendance Agent }
    LeaveBalance:
      type: object
      properties:
        type: { type: string, example: "earned" }
        available: { type: number }
        accruedThisYear: { type: number }
        carriedForward: { type: number }
        pendingRequests: { type: number }
    LeaveRequestInput:
      type: object
      required: [employeeId, type, from, to]
      properties:
        employeeId: { type: string }
        type: { type: string, example: "earned" }
        from: { type: string, format: date }
        to: { type: string, format: date }
        reason: { type: string, maxLength: 500 }
    RequestTicket:
      type: object
      properties:
        requestId: { type: string }
        status: { type: string, enum: [pending, auto_approved, approved, rejected, escalated] }
        decidedBy: { type: string, description: "policy | approver email | null while pending" }
        auditRef: { type: string, description: Immutable audit log reference }
    PayrollRun:
      type: object
      properties:
        id: { type: string }
        period: { type: string, example: "2026-05" }
        status: { type: string, enum: [draft, previewed, executed, filed] }
        employees: { type: integer }
        grossTotal: { type: number, description: Requires payroll:read scope }
        executedAt: { type: string, format: date-time }
    PayrollPreview:
      type: object
      properties:
        period: { type: string }
        employees: { type: integer }
        grossTotal: { type: number }
        statutory:
          type: object
          properties:
            pf: { type: number }
            esi: { type: number }
            pt: { type: number }
            gratuityAccrual: { type: number }
        lopDeductions: { type: number }
        overtimePay: { type: number }
        lines:
          type: array
          items:
            type: object
            properties:
              employeeId: { type: string }
              gross: { type: number }
              lopDays: { type: number }
              net: { type: number }
    SkillMatch:
      type: object
      properties:
        employeeId: { type: string }
        name: { type: string }
        matchedSkills: { type: array, items: { type: string } }
        allocation: { type: string, example: "70% on Project Atlas until 2026-07-15" }
        availableFrom: { type: string, format: date }
        score: { type: number, minimum: 0, maximum: 1 }
    WebhookSubscription:
      type: object
      properties:
        id: { type: string }
        url: { type: string, format: uri }
        events: { type: array, items: { type: string } }
        secret: { type: string, description: Only present on creation }
        active: { type: boolean }
    Error:
      type: object
      properties:
        error: { type: string, example: "validation_failed" }
        message: { type: string }
        details: { type: array, items: { type: string } }
