openapi: 3.1.0
info:
  title: CompanyBrain API Karyawan (lengkap)
  version: 1.1.0
  description: 'Seluruh endpoint yang dapat dipanggil seorang karyawan, dibangkitkan langsung dari kode
    backend oleh tool/bangkitkan.py.


    Perusahaan ditentukan dari header Host. Ganti variabel server `host` dengan host perusahaan yang dituju.


    Endpoint yang menuntut izin di luar jangkauan karyawan biasa TIDAK dimuat di sini; daftarnya ada di
    halaman naratif.


    Ringkasan dan keterangan diambil dari komentar dokumentasi di kode. Yang tidak punya komentar hanya
    memuat metode dan jalurnya.'
servers:
- url: https://{host}/api/v1
  variables:
    host:
      default: os.perusahaan-contoh.id
      description: Host perusahaan. Menentukan tenant.
tags:
- name: Publik dan masuk
  description: Dipanggil sebelum ada token, dan saat berpindah perusahaan.
- name: Akun saya
  description: Profil, PIN, keamanan, notifikasi, slip gaji.
- name: Kehadiran
  description: Absen, riwayat, shift, lembur.
- name: Pengajuan
  description: Cuti, lembur, reimbursement, kasbon.
- name: Persetujuan
  description: Bagi karyawan yang juga menjadi atasan.
- name: Tugas dan proyek
  description: Pekerjaan harian, daftar periksa, komentar, timesheet.
- name: Info dan komunikasi
  description: Pengumuman, diskusi, jajak pendapat, survei, pengetahuan.
- name: Email
  description: Kotak surat perusahaan. Setiap peran memilikinya.
- name: Berkas
  description: Unggah dan unduh lampiran.
- name: Lapangan
  description: 'Hanya untuk peran yang memilikinya: pengemudi, gudang, produksi, QC.'
- name: Direktori
  description: Data karyawan, departemen, tim, jabatan, cabang.
security:
- bearer: []
paths:
  /auth/employee-login:
    post:
      tags:
      - Publik dan masuk
      summary: POST /auth/employee-login Seluruh kegagalan yang berkaitan dengan identitas dijawab dengan
        pesan yang sama
      operationId: employee_auth_masuk_post
      description: 'Tanpa pemeriksaan izin; cukup token yang sah.


        POST /auth/employee-login Seluruh kegagalan yang berkaitan dengan identitas dijawab dengan pesan
        yang sama. Membedakan "kode tidak ada" dari "PIN salah" akan mengubah endpoint ini menjadi alat
        untuk memastikan kode karyawan mana yang berlaku di sebuah perusahaan.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MasukReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  tokens: {}
                  tenant_id: {}
                  employee_id: {}
                  must_change_pin: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /auth/login:
    post:
      tags:
      - Publik dan masuk
      summary: POST /auth/login
      operationId: auth_login_post
      description: Tanpa pemeriksaan izin; cukup token yang sah.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LoginReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  requires_2fa:
                    type: boolean
                  tokens: {}
                  tenant_id: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /auth/refresh:
    post:
      tags:
      - Publik dan masuk
      summary: POST /auth/refresh
      operationId: auth_refresh_post
      description: Tanpa pemeriksaan izin; cukup token yang sah.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RefreshReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenPair'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /auth/switch-tenant:
    post:
      tags:
      - Publik dan masuk
      summary: 'Tukar tenant aktif: terbitkan token baru untuk tenant lain milik user'
      operationId: auth_switch_tenant_post
      description: 'Tanpa pemeriksaan izin; cukup token yang sah.


        Tukar tenant aktif: terbitkan token baru untuk tenant lain milik user.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SwitchReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  tokens: {}
                  tenant_id: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /avatars/{filename}:
    get:
      tags:
      - Publik dan masuk
      summary: GET /avatars/{filename} — publik (img tag tidak bisa kirim header auth)
      operationId: profile_serve_avatar_get
      parameters:
      - name: filename
        in: path
        required: true
        schema:
          type: string
      description: 'Tanpa pemeriksaan izin; cukup token yang sah.


        GET /avatars/{filename} — publik (img tag tidak bisa kirim header auth).'
      responses:
        '200':
          description: Berkas
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /branding-assets/{tenant}/{filename}:
    get:
      tags:
      - Publik dan masuk
      summary: GET /branding-assets/{tenant}/{filename} — public (login page <img>)
      operationId: branding_serve_image_get
      parameters:
      - name: tenant
        in: path
        required: true
        schema:
          type: string
      - name: filename
        in: path
        required: true
        schema:
          type: string
      description: 'Tanpa pemeriksaan izin; cukup token yang sah.


        GET /branding-assets/{tenant}/{filename} — public (login page <img>).'
      responses:
        '200':
          description: Berkas
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /my/tenants:
    get:
      tags:
      - Publik dan masuk
      summary: Daftar tenant yang dimiliki user (untuk switcher)
      operationId: auth_my_tenants_get
      description: 'Tanpa pemeriksaan izin; cukup token yang sah.


        Daftar tenant yang dimiliki user (untuk switcher).'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /public/branding:
    get:
      tags:
      - Publik dan masuk
      summary: GET /public/branding — unauthenticated
      operationId: branding_public_branding_get
      description: 'Tanpa pemeriksaan izin; cukup token yang sah.


        GET /public/branding — unauthenticated. Returns branding for the company whose custom domain /
        subdomain matches the request host (for the login page).'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  found:
                    type: boolean
                  name: {}
                  slug: {}
                  branding: {}
                  portal: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /push/vapid-public-key:
    get:
      tags:
      - Publik dan masuk
      summary: GET /push/vapid-public-key — kunci publik VAPID untuk browser (publik)
      operationId: profile_vapid_public_key_get
      description: 'Tanpa pemeriksaan izin; cukup token yang sah.


        GET /push/vapid-public-key — kunci publik VAPID untuk browser (publik).'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  key: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /tenant/branding:
    get:
      tags:
      - Publik dan masuk
      summary: GET /tenant/branding — current company's branding + domain config (authed)
      operationId: branding_get_branding_get
      description: 'Tanpa pemeriksaan izin; cukup token yang sah.


        GET /tenant/branding — current company''s branding + domain config (authed).'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  name: {}
                  slug: {}
                  subdomain: {}
                  custom_domain: {}
                  branding: {}
                  root_domain: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /devices/fcm:
    delete:
      tags:
      - Akun saya
      summary: DELETE /devices/fcm — remove an FCM token (e
      operationId: devices_unregister_fcm_delete
      description: 'Tanpa pemeriksaan izin; cukup token yang sah.


        DELETE /devices/fcm — remove an FCM token (e.g. on logout). Scoped to the caller''s tenant via
        RLS; idempotent (deleting an unknown token returns deleted=0).'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UnregisterFcmReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    post:
      tags:
      - Akun saya
      summary: POST /devices/fcm — upsert the caller's FCM token (keyed by `token`)
      operationId: devices_register_fcm_post
      description: 'Tanpa pemeriksaan izin; cukup token yang sah.


        POST /devices/fcm — upsert the caller''s FCM token (keyed by `token`). Re-registering an existing
        token re-owns it to the current user/employee/tenant and refreshes `platform` + `updated_at`.
        `user_id` from auth, `employee_id` from actor_ctx (nullable), `tenant_id` from auth.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RegisterFcmReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  id: {}
                  registered:
                    type: boolean
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /me:
    get:
      tags:
      - Akun saya
      summary: GET /me
      operationId: me_me_get
      description: Tanpa pemeriksaan izin; cukup token yang sah.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  user: {}
                  employee_id: {}
                  employee:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  tenant: {}
                  roles:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  permissions:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  denials:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  modules:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  is_platform_admin: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /me/2fa/disable:
    post:
      tags:
      - Akun saya
      summary: POST /me/2fa/disable {code} → matikan 2FA (verifikasi kode TOTP atau recovery)
      operationId: profile_twofa_disable_post
      description: 'Tanpa pemeriksaan izin; cukup token yang sah.


        POST /me/2fa/disable {code} → matikan 2FA (verifikasi kode TOTP atau recovery).'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CodeReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /me/2fa/enable:
    post:
      tags:
      - Akun saya
      summary: POST /me/2fa/enable {code} → verifikasi & aktifkan, kembalikan recovery codes
      operationId: profile_twofa_enable_post
      description: 'Tanpa pemeriksaan izin; cukup token yang sah.


        POST /me/2fa/enable {code} → verifikasi & aktifkan, kembalikan recovery codes.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CodeReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  recovery_codes: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /me/2fa/setup:
    post:
      tags:
      - Akun saya
      summary: POST /me/2fa/setup → buat secret baru (belum aktif), kembalikan secret + otpauth URL
      operationId: profile_twofa_setup_post
      description: 'Tanpa pemeriksaan izin; cukup token yang sah.


        POST /me/2fa/setup → buat secret baru (belum aktif), kembalikan secret + otpauth URL.'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  secret: {}
                  otpauth_url: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /me/app-pin:
    get:
      tags:
      - Akun saya
      summary: 'GET /me/app-pin : apakah pemakai ini sudah punya PIN'
      operationId: employee_auth_status_saya_get
      description: 'Tanpa pemeriksaan izin; cukup token yang sah.


        GET /me/app-pin : apakah pemakai ini sudah punya PIN. Dipakai aplikasi untuk memutuskan apakah
        menawarkan "buat PIN" pada orang yang masuk lewat surel, sehingga kunjungan berikutnya cukup dengan
        PIN.


        Bentuk balasan dibaca tangan dari kode, bukan dari struct.'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  punya_data_karyawan:
                    type: boolean
                  employee_code:
                    type: string
                  punya_pin:
                    type: boolean
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    put:
      tags:
      - Akun saya
      summary: 'PUT /me/app-pin : karyawan mengganti PIN-nya sendiri'
      operationId: employee_auth_ganti_put
      description: 'Tanpa pemeriksaan izin; cukup token yang sah.


        PUT /me/app-pin : karyawan mengganti PIN-nya sendiri.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GantiReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /me/avatar:
    delete:
      tags:
      - Akun saya
      summary: DELETE /me/avatar — reset/hapus foto profil
      operationId: profile_delete_avatar_delete
      description: 'Tanpa pemeriksaan izin; cukup token yang sah.


        DELETE /me/avatar — reset/hapus foto profil.'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    post:
      tags:
      - Akun saya
      summary: POST /me/avatar
      operationId: profile_upload_avatar_post
      description: Tanpa pemeriksaan izin; cukup token yang sah.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  avatar_url: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /me/history:
    get:
      tags:
      - Akun saya
      summary: GET /me/history — driver/helper activity summary for the actor's linked employee
      operationId: me_history_get
      description: 'Tanpa pemeriksaan izin; cukup token yang sah.


        GET /me/history — driver/helper activity summary for the actor''s linked employee. Auth only (no
        RBAC `require`): a user can always see their own history. Resolves the actor''s employee via `actor_ctx`;
        if none is linked, returns zeros/empties. Returns: { trips_completed, points_total, recent:[{trip_id,
        ended_at, points}] (last 10) }'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  trips_completed:
                    type: integer
                  points_total:
                    type: number
                  recent: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /me/password:
    post:
      tags:
      - Akun saya
      summary: POST /me/password
      operationId: profile_change_password_post
      description: Tanpa pemeriksaan izin; cukup token yang sah.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PasswordReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /me/payslips:
    get:
      tags:
      - Akun saya
      summary: GET /me/payslips Slip gaji milik karyawan ini, dari seluruh periode yang sudah difinalkan
      operationId: my_work_slip_saya_get
      parameters:
      - name: per_page
        in: query
        required: false
        schema: &id092
          type: integer
      description: 'Tanpa pemeriksaan izin; cukup token yang sah.


        GET /me/payslips Slip gaji milik karyawan ini, dari seluruh periode yang sudah difinalkan. Aplikasi
        karyawan sebelumnya menempuh jalan yang dipakai layar kantor: mengambil daftar periode penggajian
        lebih dahulu, lalu slip di dalamnya. Jalan itu keliru untuk karyawan biasa, sebab daftar periode
        bersifat seisi perusahaan dan tidak dapat disaring per orang, sehingga membukanya berarti memberi
        tahu setiap karyawan kapan saja perusahaan menggaji dan berapa banyak periode yang ada. Periode
        yang belum final sengaja tidak ditampilkan. Angkanya masih dapat berubah, dan slip yang berubah
        sesudah dilihat menimbulkan pertanyaan yang tidak perlu.'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: {}
                  punya_data_karyawan:
                    type: boolean
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /me/preferences:
    get:
      tags:
      - Akun saya
      summary: GET /me/preferences
      operationId: profile_get_preferences_get
      description: 'Tanpa pemeriksaan izin; cukup token yang sah.


        Bentuk balasan dibaca tangan dari kode, bukan dari struct.'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    put:
      tags:
      - Akun saya
      summary: PUT /me/preferences
      operationId: profile_update_preferences_put
      description: 'Tanpa pemeriksaan izin; cukup token yang sah.


        Bentuk balasan dibaca tangan dari kode, bukan dari struct.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /me/push/subscribe:
    delete:
      tags:
      - Akun saya
      summary: DELETE /me/push/subscribe — hapus subscription
      operationId: profile_push_unsubscribe_delete
      description: 'Tanpa pemeriksaan izin; cukup token yang sah.


        DELETE /me/push/subscribe — hapus subscription.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PushUnsubReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    post:
      tags:
      - Akun saya
      summary: POST /me/push/subscribe — simpan/segarkan subscription web push milik user
      operationId: profile_push_subscribe_post
      description: 'Tanpa pemeriksaan izin; cukup token yang sah.


        POST /me/push/subscribe — simpan/segarkan subscription web push milik user.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PushSubReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /me/tasks:
    get:
      tags:
      - Akun saya
      summary: 'GET /me/tasks Tugas yang ditugaskan kepada karyawan milik pemakai ini, dari seluruh proyek,
        diurutkan menurut '
      operationId: my_work_tugas_saya_get
      parameters:
      - name: selesai
        in: query
        required: false
        schema: &id096
          type: boolean
      - name: per_page
        in: query
        required: false
        schema: &id097
          type: integer
      description: 'Tanpa pemeriksaan izin; cukup token yang sah.


        GET /me/tasks Tugas yang ditugaskan kepada karyawan milik pemakai ini, dari seluruh proyek, diurutkan
        menurut tenggat.'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: {}
                  punya_data_karyawan:
                    type: boolean
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /notifications:
    get:
      tags:
      - Akun saya
      summary: GET /notifications
      operationId: notifications_list_get
      parameters:
      - name: page
        in: query
        required: false
        schema: &id001
          type: integer
      - name: per_page
        in: query
        required: false
        schema: &id002
          type: integer
      description: Tanpa pemeriksaan izin; cukup token yang sah.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/NotifRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /notifications/read-all:
    post:
      tags:
      - Akun saya
      summary: POST /notifications/read-all
      operationId: notifications_mark_all_read_post
      description: Tanpa pemeriksaan izin; cukup token yang sah.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /notifications/unread-count:
    get:
      tags:
      - Akun saya
      summary: GET /notifications/unread-count
      operationId: notifications_unread_count_get
      description: Tanpa pemeriksaan izin; cukup token yang sah.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  count: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /notifications/{id}/read:
    post:
      tags:
      - Akun saya
      summary: POST /notifications/{id}/read
      operationId: notifications_mark_read_post
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Tanpa pemeriksaan izin; cukup token yang sah.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /attendance:
    get:
      tags:
      - Kehadiran
      summary: GET /attendance
      operationId: attendance_list_attendances_get
      parameters:
      - name: employee_id
        in: query
        required: false
        schema: &id024
          type: string
          format: uuid
      - name: from
        in: query
        required: false
        schema: &id025
          type: string
          format: date
      - name: to
        in: query
        required: false
        schema: &id026
          type: string
          format: date
      - name: page
        in: query
        required: false
        schema: &id027
          type: integer
      - name: per_page
        in: query
        required: false
        schema: &id028
          type: integer
      description: Menuntut izin `attendance:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/AttendanceListRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /attendance/check-in:
    post:
      tags:
      - Kehadiran
      summary: POST /attendance/check-in
      operationId: attendance_check_in_post
      description: Menuntut izin `attendance:create`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CheckInReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AttendanceRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /attendance/check-out:
    post:
      tags:
      - Kehadiran
      summary: POST /attendance/check-out
      operationId: attendance_check_out_post
      description: Menuntut izin `attendance:create, attendance:update`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CheckOutReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AttendanceRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /attendance/gateway:
    post:
      tags:
      - Kehadiran
      summary: POST /attendance/gateway
      operationId: attendance_gateway_post
      description: Tanpa pemeriksaan izin; cukup token yang sah.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GatewayReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  employee_id: {}
                  action: {}
                  method: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /attendance/overtime:
    get:
      tags:
      - Kehadiran
      summary: GET /attendance/overtime
      operationId: attendance_list_overtime_get
      parameters:
      - name: employee_id
        in: query
        required: false
        schema: &id050
          type: string
          format: uuid
      - name: from
        in: query
        required: false
        schema: &id051
          type: string
          format: date
      - name: to
        in: query
        required: false
        schema: &id052
          type: string
          format: date
      - name: page
        in: query
        required: false
        schema: &id053
          type: integer
      - name: per_page
        in: query
        required: false
        schema: &id054
          type: integer
      description: Menuntut izin `attendance:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/OvertimeRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    post:
      tags:
      - Kehadiran
      summary: POST /attendance/overtime
      operationId: attendance_submit_overtime_post
      description: Menuntut izin `attendance:create`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SubmitOvertimeReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OvertimeRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /attendance/report:
    get:
      tags:
      - Kehadiran
      summary: GET /attendance/report
      operationId: attendance_report_get
      parameters:
      - name: from
        in: query
        required: false
        schema: &id087
          type: string
          format: date
      - name: to
        in: query
        required: false
        schema: &id088
          type: string
          format: date
      description: Menuntut izin `attendance:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  from: {}
                  to: {}
                  summary: {}
                  by_day: {}
                  per_employee: {}
                  locations: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /attendance/settings:
    get:
      tags:
      - Kehadiran
      summary: GET /attendance/settings
      operationId: attendance_get_settings_get
      description: Menuntut izin `attendance:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  methods: {}
                  geofence_mode: {}
                  office_lat: {}
                  office_lng: {}
                  radius_m: {}
                  require_photo: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /attendance/shifts:
    get:
      tags:
      - Kehadiran
      summary: GET /attendance/shifts
      operationId: attendance_list_shifts_get
      description: Menuntut izin `attendance:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/ShiftRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /hr/loans:
    get:
      tags:
      - Pengajuan
      summary: GET /hr/loans
      operationId: hr_list_loans_get
      parameters:
      - name: employee_id
        in: query
        required: false
        schema: &id081
          type: string
          format: uuid
      - name: status
        in: query
        required: false
        schema: &id082
          type: string
      description: Menuntut izin `payroll:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/LoanRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    post:
      tags:
      - Pengajuan
      summary: POST /hr/loans
      operationId: hr_create_loan_post
      description: Menuntut izin `payroll:create`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateLoanReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LoanRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /hr/loans/{id}:
    patch:
      tags:
      - Pengajuan
      summary: Only cancellation / reactivation is allowed — amounts are immutable once instalments may
        already have been ded
      operationId: hr_update_loan_patch
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: 'Menuntut izin `payroll:update`.


        Only cancellation / reactivation is allowed — amounts are immutable once instalments may already
        have been deducted.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateLoanReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LoanRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /hr/loans/{id}/repayments:
    get:
      tags:
      - Pengajuan
      summary: GET /hr/loans/{id}/repayments
      operationId: hr_loan_repayments_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `payroll:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/RepaymentRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /leave-balances:
    get:
      tags:
      - Pengajuan
      summary: GET /leave-balances
      operationId: leave_get_balance_get
      parameters:
      - name: employee_id
        in: query
        required: false
        schema: &id016
          type: string
          format: uuid
      - name: leave_type_id
        in: query
        required: false
        schema: &id017
          type: string
          format: uuid
      - name: year
        in: query
        required: false
        schema: &id018
          type: integer
      description: Menuntut izin `leave:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/LeaveBalanceRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    post:
      tags:
      - Pengajuan
      summary: POST /leave-balances
      operationId: leave_set_balance_post
      description: Menuntut izin `leave:create`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetBalanceReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LeaveBalanceRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /leave-requests:
    get:
      tags:
      - Pengajuan
      summary: GET /leave-requests
      operationId: leave_list_leave_requests_get
      parameters:
      - name: employee_id
        in: query
        required: false
        schema: &id042
          type: string
          format: uuid
      - name: page
        in: query
        required: false
        schema: &id043
          type: integer
      - name: per_page
        in: query
        required: false
        schema: &id044
          type: integer
      description: Menuntut izin `leave:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/LeaveRequestListRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    post:
      tags:
      - Pengajuan
      summary: POST /leave-requests
      operationId: leave_submit_leave_post
      description: Menuntut izin `leave:create`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SubmitLeaveReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LeaveRequestRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /leave-requests/{id}:
    get:
      tags:
      - Pengajuan
      summary: GET /leave-requests/{id}
      operationId: leave_get_leave_request_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `leave:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  request: {}
                  employee: {}
                  leave_type: {}
                  balance:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  approval: {}
                  flow_steps: {}
                  timeline: {}
                  history: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /leave-types:
    get:
      tags:
      - Pengajuan
      summary: GET /leave-types
      operationId: leave_list_leave_types_get
      description: Menuntut izin `leave:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/LeaveTypeRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    post:
      tags:
      - Pengajuan
      summary: POST /leave-types
      operationId: leave_create_leave_type_post
      description: Menuntut izin `leave:create`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LeaveTypeReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LeaveTypeRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /leave-types/{id}:
    delete:
      tags:
      - Pengajuan
      summary: DELETE /leave-types/{id}
      operationId: leave_delete_leave_type_delete
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `leave:delete`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted:
                    type: boolean
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    get:
      tags:
      - Pengajuan
      summary: GET /leave-types/{id}
      operationId: leave_get_leave_type_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `leave:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  leave_type: {}
                  stats: {}
                  by_status: {}
                  monthly: {}
                  balance_summary: {}
                  top_employees: {}
                  recent: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    put:
      tags:
      - Pengajuan
      summary: PUT /leave-types/{id}
      operationId: leave_update_leave_type_put
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `leave:update`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LeaveTypeReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LeaveTypeRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /reimbursement-types:
    get:
      tags:
      - Pengajuan
      summary: GET /reimbursement-types
      operationId: reimbursement_list_types_get
      description: Menuntut izin `reimbursement:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/ReimbursementTypeRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    post:
      tags:
      - Pengajuan
      summary: POST /reimbursement-types
      operationId: reimbursement_create_type_post
      description: Menuntut izin `reimbursement:create`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateTypeReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReimbursementTypeRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /reimbursement-types/{id}:
    delete:
      tags:
      - Pengajuan
      summary: DELETE /reimbursement-types/{id}
      operationId: reimbursement_delete_type_delete
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `reimbursement:delete`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted:
                    type: boolean
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    get:
      tags:
      - Pengajuan
      summary: GET /reimbursement-types/{id}
      operationId: reimbursement_get_type_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `reimbursement:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  type: {}
                  stats: {}
                  by_status: {}
                  monthly: {}
                  top_employees: {}
                  recent: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    put:
      tags:
      - Pengajuan
      summary: PUT /reimbursement-types/{id}
      operationId: reimbursement_update_type_put
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `reimbursement:update`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateTypeReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReimbursementTypeRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /reimbursements:
    get:
      tags:
      - Pengajuan
      summary: GET /reimbursements
      operationId: reimbursement_list_get
      parameters:
      - name: page
        in: query
        required: false
        schema: *id001
      - name: per_page
        in: query
        required: false
        schema: *id002
      description: Menuntut izin `reimbursement:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/ReimbursementRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    post:
      tags:
      - Pengajuan
      summary: POST /reimbursements
      operationId: reimbursement_submit_post
      description: Menuntut izin `reimbursement:create`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SubmitReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReimbursementRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /reimbursements/{id}:
    get:
      tags:
      - Pengajuan
      summary: GET /reimbursements/{id}
      operationId: reimbursement_get_one_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `reimbursement:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReimbursementRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /approvals/inbox:
    get:
      tags:
      - Persetujuan
      summary: GET /approvals/inbox
      operationId: approvals_inbox_get
      description: Tanpa pemeriksaan izin; cukup token yang sah.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/InboxRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /approvals/pending-count:
    get:
      tags:
      - Persetujuan
      summary: Jumlah approval yang menunggu keputusan aktor — untuk lencana di sidebar
      operationId: approvals_pending_count_get
      description: 'Tanpa pemeriksaan izin; cukup token yang sah.


        Jumlah approval yang menunggu keputusan aktor — untuk lencana di sidebar.'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  count: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /approvals/{id}:
    get:
      tags:
      - Persetujuan
      summary: GET /approvals/{id}
      operationId: approvals_get_request_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Tanpa pemeriksaan izin; cukup token yang sah.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApprovalRequest'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /approvals/{id}/act:
    post:
      tags:
      - Persetujuan
      summary: POST /approvals/{id}/act
      operationId: approvals_act_post
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Tanpa pemeriksaan izin; cukup token yang sah.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ActReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApprovalRequest'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /projects:
    get:
      tags:
      - Tugas dan proyek
      summary: GET /projects
      operationId: work_list_projects_get
      parameters:
      - name: page
        in: query
        required: false
        schema: &id055
          type: integer
      - name: per_page
        in: query
        required: false
        schema: &id056
          type: integer
      - name: status
        in: query
        required: false
        schema: &id057
          type: string
      description: Menuntut izin `project:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/ProjectRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /projects/{project_id}:
    get:
      tags:
      - Tugas dan proyek
      summary: GET /projects/{project_id}
      operationId: work_get_project_get
      parameters:
      - name: project_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `project:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/TaskStatusRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /projects/{project_id}/columns:
    get:
      tags:
      - Tugas dan proyek
      summary: GET /projects/{project_id}/columns
      operationId: work_list_columns_get
      parameters:
      - name: project_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `project:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/TaskStatusRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /projects/{project_id}/members:
    get:
      tags:
      - Tugas dan proyek
      summary: GET /projects/{project_id}/members
      operationId: work_list_project_members_get
      parameters:
      - name: project_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `project:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /projects/{project_id}/tasks:
    get:
      tags:
      - Tugas dan proyek
      summary: GET /projects/{project_id}/tasks
      operationId: work_list_tasks_get
      parameters:
      - name: project_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      - name: status_id
        in: query
        required: false
        schema: &id065
          type: string
          format: uuid
      - name: assignee_employee_id
        in: query
        required: false
        schema: &id066
          type: string
          format: uuid
      - name: page
        in: query
        required: false
        schema: &id067
          type: integer
      - name: per_page
        in: query
        required: false
        schema: &id068
          type: integer
      description: Menuntut izin `task:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/TaskRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /projects/{project_id}/tasks/{task_id}:
    get:
      tags:
      - Tugas dan proyek
      summary: GET /projects/{project_id}/tasks/{task_id}
      operationId: work_get_task_get
      parameters:
      - name: project_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      - name: task_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `task:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    put:
      tags:
      - Tugas dan proyek
      summary: PUT /projects/{project_id}/tasks/{task_id}
      operationId: work_update_task_put
      parameters:
      - name: project_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      - name: task_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `task:update`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateTaskReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /projects/{project_id}/tasks/{task_id}/move:
    patch:
      tags:
      - Tugas dan proyek
      summary: PATCH /projects/{project_id}/tasks/{task_id}/move
      operationId: work_move_task_patch
      parameters:
      - name: project_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      - name: task_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `task:update`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MoveTaskReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /sprints:
    get:
      tags:
      - Tugas dan proyek
      summary: GET /sprints
      operationId: sprints_list_sprints_get
      parameters:
      - name: project_id
        in: query
        required: false
        schema: &id061
          type: string
          format: uuid
      - name: page
        in: query
        required: false
        schema: &id062
          type: integer
      - name: per_page
        in: query
        required: false
        schema: &id063
          type: integer
      description: Menuntut izin `sprints:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/SprintRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /sprints/{id}:
    get:
      tags:
      - Tugas dan proyek
      summary: GET /sprints/{id}
      operationId: sprints_get_sprint_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `sprints:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SprintRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /sprints/{id}/board:
    get:
      tags:
      - Tugas dan proyek
      summary: GET /sprints/{id}/board
      operationId: sprints_sprint_board_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `sprints:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/BoardTaskRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /sprints/{id}/burndown:
    get:
      tags:
      - Tugas dan proyek
      summary: GET /sprints/{id}/burndown
      operationId: sprints_sprint_burndown_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `sprints:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  start_date: {}
                  end_date: {}
                  total_tasks: {}
                  done_tasks: {}
                  remaining_tasks: {}
                  ideal: {}
                  days: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /tasks/{task_id}/checklist:
    get:
      tags:
      - Tugas dan proyek
      summary: GET /tasks/{task_id}/checklist
      operationId: work_list_checklist_get
      parameters:
      - name: task_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `task:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/ChecklistItemRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /tasks/{task_id}/checklist/{item_id}/toggle:
    patch:
      tags:
      - Tugas dan proyek
      summary: PATCH /tasks/{task_id}/checklist/{item_id}/toggle
      operationId: work_toggle_checklist_item_patch
      parameters:
      - name: task_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      - name: item_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `task:update`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ChecklistItemRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /tasks/{task_id}/comments:
    get:
      tags:
      - Tugas dan proyek
      summary: GET /tasks/{task_id}/comments
      operationId: work_list_comments_get
      parameters:
      - name: task_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `task:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/TaskCommentRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /tasks/{task_id}/watch:
    delete:
      tags:
      - Tugas dan proyek
      summary: DELETE /tasks/{task_id}/watch
      operationId: work_unwatch_task_delete
      parameters:
      - name: task_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `task:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  watching:
                    type: boolean
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    post:
      tags:
      - Tugas dan proyek
      summary: POST /tasks/{task_id}/watch
      operationId: work_watch_task_post
      parameters:
      - name: task_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `task:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  watching:
                    type: boolean
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /tasks/{task_id}/watchers:
    get:
      tags:
      - Tugas dan proyek
      summary: GET /tasks/{task_id}/watchers
      operationId: work_list_watchers_get
      parameters:
      - name: task_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `task:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  watching: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /timesheets:
    get:
      tags:
      - Tugas dan proyek
      summary: GET /timesheets
      operationId: timesheet_list_get
      parameters:
      - name: page
        in: query
        required: false
        schema: *id001
      - name: per_page
        in: query
        required: false
        schema: *id002
      description: Menuntut izin `timesheet:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/TimesheetRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    post:
      tags:
      - Tugas dan proyek
      summary: POST /timesheets
      operationId: timesheet_create_post
      description: Menuntut izin `timesheet:create`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TimesheetRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /timesheets/weekly:
    get:
      tags:
      - Tugas dan proyek
      summary: GET /timesheets/weekly
      operationId: timesheet_weekly_summary_get
      parameters:
      - name: employee_id
        in: query
        required: true
        schema: &id098
          type: string
          format: uuid
      - name: week_start
        in: query
        required: true
        schema: &id099
          type: string
          format: date
      description: Menuntut izin `timesheet:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  employee_id: {}
                  week_start: {}
                  week_end: {}
                  days: {}
                  grand_total_minutes: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /announcements:
    get:
      tags:
      - Info dan komunikasi
      summary: GET /announcements
      operationId: comms_list_announcements_get
      parameters:
      - name: is_active
        in: query
        required: false
        schema: &id008
          type: boolean
      - name: page
        in: query
        required: false
        schema: &id009
          type: integer
      - name: per_page
        in: query
        required: false
        schema: &id010
          type: integer
      description: Menuntut izin `comms:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/AnnouncementRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /announcements/{id}:
    get:
      tags:
      - Info dan komunikasi
      summary: GET /announcements/{id}
      operationId: comms_get_announcement_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `comms:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AnnouncementRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /knowledge/articles:
    get:
      tags:
      - Info dan komunikasi
      summary: GET /knowledge/articles
      operationId: knowledge_list_articles_get
      parameters:
      - name: space_id
        in: query
        required: false
        schema: &id011
          type: string
          format: uuid
      - name: kind
        in: query
        required: false
        schema: &id012
          type: string
      - name: status
        in: query
        required: false
        schema: &id013
          type: string
      - name: page
        in: query
        required: false
        schema: &id014
          type: integer
      - name: per_page
        in: query
        required: false
        schema: &id015
          type: integer
      description: Menuntut izin `knowledge:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/ArticleRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /knowledge/articles/by-tag:
    get:
      tags:
      - Info dan komunikasi
      summary: GET /knowledge/articles/by-tag
      operationId: knowledge_list_articles_by_tag_get
      parameters:
      - name: tag
        in: query
        required: false
        schema: &id093
          type: string
      - name: page
        in: query
        required: false
        schema: &id094
          type: integer
      - name: per_page
        in: query
        required: false
        schema: &id095
          type: integer
      description: Menuntut izin `knowledge:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/ArticleRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /knowledge/articles/{id}:
    get:
      tags:
      - Info dan komunikasi
      summary: GET /knowledge/articles/{id}
      operationId: knowledge_get_article_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `knowledge:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ArticleRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /knowledge/articles/{id}/versions:
    get:
      tags:
      - Info dan komunikasi
      summary: GET /knowledge/articles/{id}/versions
      operationId: knowledge_list_versions_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `knowledge:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/VersionRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /knowledge/search:
    get:
      tags:
      - Info dan komunikasi
      summary: GET /knowledge/search
      operationId: knowledge_search_articles_get
      parameters:
      - name: q
        in: query
        required: false
        schema: &id089
          type: string
      - name: page
        in: query
        required: false
        schema: &id090
          type: integer
      - name: per_page
        in: query
        required: false
        schema: &id091
          type: integer
      description: Menuntut izin `knowledge:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/ArticleRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /knowledge/spaces:
    get:
      tags:
      - Info dan komunikasi
      summary: GET /knowledge/spaces
      operationId: knowledge_list_spaces_get
      description: Menuntut izin `knowledge:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/SpaceRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /knowledge/spaces/{id}:
    get:
      tags:
      - Info dan komunikasi
      summary: GET /knowledge/spaces/{id}
      operationId: knowledge_get_space_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `knowledge:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SpaceRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /polls:
    get:
      tags:
      - Info dan komunikasi
      summary: GET /polls
      operationId: comms_list_polls_get
      parameters:
      - name: page
        in: query
        required: false
        schema: &id003
          type: integer
      - name: per_page
        in: query
        required: false
        schema: &id004
          type: integer
      description: Menuntut izin `comms:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/PollRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /polls/{id}:
    get:
      tags:
      - Info dan komunikasi
      summary: GET /polls/{id}
      operationId: comms_get_poll_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `comms:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/PollOptionRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /surveys:
    get:
      tags:
      - Info dan komunikasi
      summary: GET /surveys
      operationId: comms_list_surveys_get
      parameters:
      - name: page
        in: query
        required: false
        schema: *id003
      - name: per_page
        in: query
        required: false
        schema: *id004
      description: Menuntut izin `comms:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/SurveyRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /surveys/{id}:
    get:
      tags:
      - Info dan komunikasi
      summary: GET /surveys/{id}
      operationId: comms_get_survey_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `comms:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/SurveyQuestionRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /surveys/{id}/responses:
    get:
      tags:
      - Info dan komunikasi
      summary: GET /surveys/{id}/responses
      operationId: comms_list_survey_responses_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      - name: page
        in: query
        required: false
        schema: *id003
      - name: per_page
        in: query
        required: false
        schema: *id004
      description: Menuntut izin `comms:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/SurveyResponseRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /threads:
    get:
      tags:
      - Info dan komunikasi
      summary: GET /threads
      operationId: comms_list_threads_get
      parameters:
      - name: page
        in: query
        required: false
        schema: *id003
      - name: per_page
        in: query
        required: false
        schema: *id004
      description: Menuntut izin `comms:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/ThreadRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /threads/{id}:
    get:
      tags:
      - Info dan komunikasi
      summary: GET /threads/{id}
      operationId: comms_get_thread_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `comms:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/ThreadMessageRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /threads/{id}/messages:
    get:
      tags:
      - Info dan komunikasi
      summary: GET /threads/{id}/messages
      operationId: comms_list_messages_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      - name: page
        in: query
        required: false
        schema: *id003
      - name: per_page
        in: query
        required: false
        schema: *id004
      description: Menuntut izin `comms:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/ThreadMessageRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /mail/account:
    get:
      tags:
      - Email
      summary: GET /mail/account
      operationId: mailbox_account_status_get
      description: 'Menuntut izin `mailbox:read`.


        Bentuk balasan dibaca tangan dari kode, bukan dari struct.'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  module_enabled:
                    type: boolean
                  configured:
                    type: boolean
                  email:
                    type: string
                  status:
                    type: string
                  last_error:
                    type: string
                  last_verified_at:
                    type: string
                    format: date-time
                  has_password:
                    type: boolean
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /mail/account/connect:
    post:
      tags:
      - Email
      summary: POST /mail/account/connect
      operationId: mailbox_connect_account_post
      description: Menuntut izin `mailbox:update`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConnectReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  status:
                    type: string
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /mail/account/verify:
    post:
      tags:
      - Email
      summary: POST /mail/account/verify
      operationId: mailbox_verify_account_post
      description: Menuntut izin `mailbox:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /mail/admin/accounts/{membership_id}:
    put:
      tags:
      - Email
      summary: PUT /mail/admin/accounts/{membership_id}
      operationId: mailbox_map_account_put
      parameters:
      - name: membership_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `member:update`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MapAccountReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  id: {}
                  status:
                    type: string
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /mail/admin/cpanel-accounts:
    get:
      tags:
      - Email
      summary: GET /mail/admin/cpanel-accounts
      operationId: mailbox_cpanel_accounts_get
      description: Menuntut izin `member:update`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /mail/contacts:
    get:
      tags:
      - Email
      summary: GET /mail/contacts — saran penerima dari dua sumber sekaligus
      operationId: mailbox_contacts_get
      parameters:
      - name: q
        in: query
        required: false
        schema: &id019
          type: string
      - name: limit
        in: query
        required: false
        schema: &id020
          type: integer
      description: 'Menuntut izin `mailbox:read`.


        GET /mail/contacts — saran penerima dari dua sumber sekaligus. Sumber pertama adalah seluruh karyawan
        aktif yang punya alamat email, yaitu buku alamat internal perusahaan. Sumber kedua adalah alamat
        luar yang orang ini sendiri pernah kirimi, dari `mail_contacts`. Riwayat sengaja disaring pada
        `membership_id` pemanggil, bukan pada tenant. Daftar karyawan memang milik bersama, tetapi kepada
        siapa seseorang pernah mengirim email tidak.'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /mail/folders:
    get:
      tags:
      - Email
      summary: GET /mail/folders
      operationId: mailbox_folders_get
      description: Menuntut izin `mailbox:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /mail/messages:
    get:
      tags:
      - Email
      summary: GET /mail/messages
      operationId: mailbox_messages_get
      parameters:
      - name: folder
        in: query
        required: false
        schema: &id083
          type: string
      - name: page
        in: query
        required: false
        schema: &id084
          type: integer
      - name: per_page
        in: query
        required: false
        schema: &id085
          type: integer
      description: 'Menuntut izin `mailbox:read`.


        Bentuk balasan dibaca tangan dari kode, bukan dari struct.'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /mail/messages/{uid}:
    delete:
      tags:
      - Email
      summary: DELETE /mail/messages/{uid}
      operationId: mailbox_delete_message_delete
      parameters:
      - name: uid
        in: path
        required: true
        schema:
          type: string
          format: uuid
      - name: folder
        in: query
        required: true
        schema: &id005
          type: string
      description: Menuntut izin `mailbox:delete`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    get:
      tags:
      - Email
      summary: GET /mail/messages/{uid}
      operationId: mailbox_message_detail_get
      parameters:
      - name: uid
        in: path
        required: true
        schema:
          type: string
          format: uuid
      - name: folder
        in: query
        required: true
        schema: *id005
      description: 'Menuntut izin `mailbox:read`.


        Bentuk balasan dibaca tangan dari kode, bukan dari struct.'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  uid:
                    type: integer
                  subject:
                    type: string
                  from:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  to:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  cc:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  reply_to:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  date:
                    type: string
                    format: date-time
                  flags:
                    type: array
                    items:
                      type: string
                  size:
                    type: integer
                  snippet:
                    type: string
                  has_attachments:
                    type: boolean
                  body_text:
                    type: string
                  body_html:
                    type: string
                  attachments:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    patch:
      tags:
      - Email
      summary: PATCH /mail/messages/{uid}
      operationId: mailbox_message_action_patch
      parameters:
      - name: uid
        in: path
        required: true
        schema:
          type: string
          format: uuid
      - name: folder
        in: query
        required: true
        schema: *id005
      description: Menuntut izin `mailbox:update`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MessageActionReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /mail/messages/{uid}/attachments/{index}:
    get:
      tags:
      - Email
      summary: GET /mail/messages/{uid}/attachments/{index}
      operationId: mailbox_attachment_get
      parameters:
      - name: uid
        in: path
        required: true
        schema:
          type: string
          format: uuid
      - name: index
        in: path
        required: true
        schema:
          type: string
      - name: folder
        in: query
        required: true
        schema: *id005
      description: Menuntut izin `mailbox:read`.
      responses:
        '200':
          description: Berkas
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /mail/send:
    post:
      tags:
      - Email
      summary: POST /mail/send
      operationId: mailbox_send_post
      description: 'Menuntut izin `mailbox:read`.


        Bentuk balasan dibaca tangan dari kode, bukan dari struct.'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  sent:
                    type: boolean
                  duplicate:
                    type: boolean
                  copy_pending:
                    type: boolean
                  message_id:
                    type: string
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /files:
    get:
      tags:
      - Berkas
      summary: GET /files
      operationId: files_list_get
      parameters:
      - name: page
        in: query
        required: false
        schema: *id001
      - name: per_page
        in: query
        required: false
        schema: *id002
      description: Menuntut izin `file:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/FileRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    post:
      tags:
      - Berkas
      summary: POST /files
      operationId: files_upload_post
      description: Menuntut izin `file:create`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  id: {}
                  name: {}
                  mime: {}
                  size_bytes: {}
                  url: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /files/{id}:
    delete:
      tags:
      - Berkas
      summary: DELETE /files/{id}
      operationId: files_delete_delete
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `file:delete`.
      responses:
        '200':
          description: Berkas
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /files/{id}/download:
    get:
      tags:
      - Berkas
      summary: GET /files/{id}/download
      operationId: files_download_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `file:read`.
      responses:
        '200':
          description: Berkas
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /briefings:
    get:
      tags:
      - Lapangan
      summary: GET /briefings — filters for_date?, trip_id?; paginated
      operationId: field_ops_list_briefings_get
      parameters:
      - name: for_date
        in: query
        required: false
        schema: &id029
          type: string
          format: date
      - name: trip_id
        in: query
        required: false
        schema: &id030
          type: string
          format: uuid
      - name: page
        in: query
        required: false
        schema: &id031
          type: integer
      - name: per_page
        in: query
        required: false
        schema: &id032
          type: integer
      description: 'Menuntut izin `briefing:read`.


        GET /briefings — filters for_date?, trip_id?; paginated.'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/BriefingRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    post:
      tags:
      - Lapangan
      summary: POST /briefings — `{trip_id?, title, body?, for_date?}`
      operationId: field_ops_create_briefing_post
      description: 'Menuntut izin `briefing:create`.


        POST /briefings — `{trip_id?, title, body?, for_date?}`. supervisor from actor.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateBriefingReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BriefingRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /briefings/{id}:
    get:
      tags:
      - Lapangan
      summary: GET /briefings/{id} — briefing + ack list
      operationId: field_ops_get_briefing_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: 'Menuntut izin `briefing:read`.


        GET /briefings/{id} — briefing + ack list. Returns `{ briefing, acks: [...] }`.'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/BriefingAckRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /briefings/{id}/ack:
    post:
      tags:
      - Lapangan
      summary: POST /briefings/{id}/ack — `{lat?, lng?}`
      operationId: field_ops_ack_briefing_post
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: 'Menuntut izin `briefing:read`.


        POST /briefings/{id}/ack — `{lat?, lng?}`. employee from actor; upsert on the UNIQUE(tenant, briefing,
        employee) constraint (re-ack refreshes lat/lng/at).'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AckBriefingReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BriefingAckRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /distribution/delayed:
    get:
      tags:
      - Lapangan
      summary: GET /distribution/delayed (require delivery_order:read)
      operationId: dist_reports_delayed_get
      description: 'Menuntut izin `delivery_order:read`.


        GET /distribution/delayed (require delivery_order:read). All overdue DOs: due_at < NOW() and status
        not terminal (completed/delivered/ cancelled). `days_late` = floor((NOW() - due_at) in days).
        Ordered by due_at asc (most overdue first).'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/DelayedRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /distribution/do/{id}/print:
    get:
      tags:
      - Lapangan
      summary: GET /distribution/do/{id}/print
      operationId: do_print_print_delivery_order_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `delivery_order:read, sales_order:read`.
      responses:
        '200':
          description: Berkas
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /distribution/do/{id}/sync-accurate:
    post:
      tags:
      - Lapangan
      summary: POST /distribution/do/{id}/sync-accurate — push a delivery order to Accurate
      operationId: accurate_sync_do_post
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: 'Menuntut izin `sales_order:update`.


        POST /distribution/do/{id}/sync-accurate — push a delivery order to Accurate. Requires `sales_order:update`.
        When the connector is not configured/connected returns `{status:"not_configured"}` (no error).
        Otherwise builds the payload from delivery_orders + lines and attempts the push to `{host}/accurate/api/{resource}/{action}.do`
        with both headers, honoring `faktur_mode`. The actual resource path/fields are wrapped in `//
        TODO(accurate)` and the call is best-effort: on the still-unknown resource shape it returns `{status:"stub"}`
        WITHOUT panicking. Idempotent: a DO already carrying an Accurate ref (stored under `creds.synced[<do_id>]`)
        returns `{status:"ok", already:true}`.'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  message:
                    type: string
                  already:
                    type: boolean
                  accurate_ref: {}
                  do_number: {}
                  faktur_mode: {}
                  reason: {}
                  payload: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /distribution/monitor:
    get:
      tags:
      - Lapangan
      summary: GET /distribution/monitor (require trip:read)
      operationId: dist_reports_monitor_get
      description: 'Menuntut izin `trip:read`.


        GET /distribution/monitor (require trip:read). Real-time gudang ("warehouse") wallboard aggregate:
        - `today`: DO delivery counts (delivered/in_transit/pending) for DOs created today, plus the number
        of active trips (status loading/en_route). - `leaderboard`: top-15 employees by points this calendar
        month. - `active_trips`: trips currently loading/en_route with crew/vehicle labels and stop progress.'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/MonitorLeaderRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /distribution/reports/drivers:
    get:
      tags:
      - Lapangan
      summary: GET /distribution/reports/drivers?from&to (require point:read)
      operationId: dist_reports_reports_drivers_get
      parameters:
      - name: from
        in: query
        required: false
        schema: &id006
          type: string
      - name: to
        in: query
        required: false
        schema: &id007
          type: string
      description: 'Menuntut izin `point:read`.


        GET /distribution/reports/drivers?from&to (require point:read). Per-employee distribution scorecard
        over the window. The employee set is the union of: trip drivers/helpers (trips started in window)
        and anyone with a point_ledger entry in window. Counts are computed via correlated subqueries
        against that universe. Ordered by points desc.'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/DriverReportRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /distribution/reports/summary:
    get:
      tags:
      - Lapangan
      summary: GET /distribution/reports/summary?from&to (require trip:read)
      operationId: dist_reports_reports_summary_get
      parameters:
      - name: from
        in: query
        required: false
        schema: *id006
      - name: to
        in: query
        required: false
        schema: *id007
      description: 'Menuntut izin `trip:read`.


        GET /distribution/reports/summary?from&to (require trip:read). Window-scoped DO + trip KPIs. DOs
        are scoped by `created_at` within the window; `do_delayed` is a live overdue count (due_at < NOW()
        and not terminal) independent of the window. `on_time_rate` = completed DOs delivered on/before
        due_at ÷ completed DOs, null when there are no completed DOs.'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  from: {}
                  to: {}
                  do_total: {}
                  do_completed: {}
                  do_delayed: {}
                  do_in_progress: {}
                  do_cancelled: {}
                  on_time_rate: {}
                  trips_completed: {}
                  total_points: {}
                  total_weight_kg: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /distribution/suggest-trips:
    post:
      tags:
      - Lapangan
      summary: POST /distribution/suggest-trips — propose trips for unassigned DOs
      operationId: distribution_opt_suggest_trips_post
      description: 'Menuntut izin `trip:create`.


        POST /distribution/suggest-trips — propose trips for unassigned DOs. Read-only: commits NOTHING
        (the tx is only used to read; we drop it without commit). Assumptions: * Candidate DOs: status=''created''
        AND trip_id IS NULL (tenant-scoped). * Weight: total_weight_kg when present, else Σ(unit_weight*qty)
        over lines. * Coords: ship_lat/ship_lng, else the customer''s lat/lng. DOs with neither → `unplaced`
        (still capacity-packed into bins, but flagged in `warnings`). * Origin: body origin_lat/lng if
        given, else the first candidate vehicle''s depot (home_location_id), else the centroid of placed
        DOs, else (0,0). * Vehicles: is_active AND status=''idle''; if none idle, fall back to all is_active.
        `vehicle_id` (when given) filters to that one vehicle.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SuggestTripsReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  trips:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  unplaced:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  warnings: {}
                  origin: {}
                  params: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /distribution/trips/from-suggestion:
    post:
      tags:
      - Lapangan
      summary: POST /distribution/trips/from-suggestion — commit one chosen suggestion
      operationId: distribution_opt_trip_from_suggestion_post
      description: 'Menuntut izin `trip:create`.


        POST /distribution/trips/from-suggestion — commit one chosen suggestion. Creates a single manual/planned
        trip, then for each DO (status created/assigned, tenant-scoped) creates a dropoff trip_stop (seq
        in request order) + trip_stop_dos link and sets the DO''s trip_id and status=''assigned'' (mirrors
        `distribution::attach_delivery_orders`). Returns a soft capacity report.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TripFromSuggestionReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  trip_id: {}
                  attached: {}
                  total_kg: {}
                  capacity_kg: {}
                  over_capacity: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /distribution/trips/{id}/manifest:
    get:
      tags:
      - Lapangan
      summary: GET /distribution/trips/{id}/manifest
      operationId: do_print_print_trip_manifest_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `trip:read`.
      responses:
        '200':
          description: Berkas
          content:
            text/html:
              schema:
                type: string
                format: binary
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /incidents:
    get:
      tags:
      - Lapangan
      summary: GET /incidents — filters status?, trip_id?; paginated
      operationId: field_ops_list_incidents_get
      parameters:
      - name: status
        in: query
        required: false
        schema: &id038
          type: string
      - name: trip_id
        in: query
        required: false
        schema: &id039
          type: string
          format: uuid
      - name: page
        in: query
        required: false
        schema: &id040
          type: integer
      - name: per_page
        in: query
        required: false
        schema: &id041
          type: integer
      description: 'Menuntut izin `incident:read`.


        GET /incidents — filters status?, trip_id?; paginated.'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/IncidentRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    post:
      tags:
      - Lapangan
      summary: POST /incidents — `{trip_id?, kind, severity?, description}`
      operationId: field_ops_create_incident_post
      description: 'Menuntut izin `incident:create`.


        POST /incidents — `{trip_id?, kind, severity?, description}`. reported_by from actor.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateIncidentReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IncidentRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /incidents/{id}:
    put:
      tags:
      - Lapangan
      summary: PUT /incidents/{id} — `{status?, resolution?, severity?}`
      operationId: field_ops_update_incident_put
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: 'Menuntut izin `incident:update`.


        PUT /incidents/{id} — `{status?, resolution?, severity?}`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateIncidentReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IncidentRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /point-ledger:
    get:
      tags:
      - Lapangan
      summary: GET /point-ledger
      operationId: fleet_list_point_ledger_get
      parameters:
      - name: employee_id
        in: query
        required: false
        schema: &id045
          type: string
          format: uuid
      - name: trip_id
        in: query
        required: false
        schema: &id046
          type: string
          format: uuid
      - name: kind
        in: query
        required: false
        schema: &id047
          type: string
      - name: page
        in: query
        required: false
        schema: &id048
          type: integer
      - name: per_page
        in: query
        required: false
        schema: &id049
          type: integer
      description: Menuntut izin `point:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/PointLedgerRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    post:
      tags:
      - Lapangan
      summary: Manually record a point earn/deduct (supervisor incident handling, §7
      operationId: fleet_create_point_ledger_post
      description: 'Menuntut izin `point:create`.


        Manually record a point earn/deduct (supervisor incident handling, §7.2).'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateLedgerReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PointLedgerRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /point-ledger/convert-to-payroll:
    post:
      tags:
      - Lapangan
      summary: Convert the period's net points to `payroll_components` (idempotent per period)
      operationId: points_payroll_convert_to_payroll_post
      description: 'Menuntut izin `payroll:create`.


        Convert the period''s net points to `payroll_components` (idempotent per period). For each employee
        with non-zero net points: - rupiah = round(net_points * rate)   where `rate` is the tier rate
        for the net (linear `point_value_idr` fallback; deductions use base rate) - kind   = ''allowance''
        if rupiah >= 0 else ''deduction'' - amount = abs(rupiah)            (payroll_components.amount
        is an unsigned magnitude) - name   = "Insentif Poin {period}" Idempotency: keyed on (employee_id,
        name). A re-run UPDATEs the existing component (kind + amount) instead of inserting a duplicate.
        If a previously-positive net turns negative on re-run (or vice versa), `kind` is flipped accordingly.
        Employees whose component would be rupiah == 0 are skipped (and any stale prior component for
        the period is removed so the period nets out cleanly).'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConvertReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  period: {}
                  point_value_idr: {}
                  converted: {}
                  skipped: {}
                  total_idr: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /point-ledger/leaderboard:
    get:
      tags:
      - Lapangan
      summary: Driver/helper point leaderboard aggregated over a period (§7
      operationId: fleet_leaderboard_get
      parameters:
      - name: from
        in: query
        required: false
        schema: &id021
          type: string
          format: date
      - name: to
        in: query
        required: false
        schema: &id022
          type: string
          format: date
      - name: limit
        in: query
        required: false
        schema: &id023
          type: integer
      description: 'Menuntut izin `point:read`.


        Driver/helper point leaderboard aggregated over a period (§7.3). Sums signed `points` per employee;
        `from`/`to` bound the `at` timestamp (inclusive).'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/LeaderboardRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /point-ledger/payroll-preview:
    get:
      tags:
      - Lapangan
      summary: Preview the points→rupiah conversion for a period without writing anything
      operationId: points_payroll_payroll_preview_get
      parameters:
      - name: period
        in: query
        required: true
        schema: &id086
          type: string
      description: 'Menuntut izin `payroll:read`.


        Preview the points→rupiah conversion for a period without writing anything.'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/PreviewRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /point-targets:
    get:
      tags:
      - Lapangan
      summary: GET /point-targets — list point targets, optionally filtered by period
      operationId: points_payroll_list_point_targets_get
      parameters:
      - name: period
        in: query
        required: false
        schema: &id064
          type: string
      description: 'Menuntut izin `point_target:read`.


        GET /point-targets — list point targets, optionally filtered by period.'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/PointTargetRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /production-runs:
    get:
      tags:
      - Lapangan
      summary: GET /production-runs
      operationId: production_list_production_runs_get
      parameters:
      - name: batch_id
        in: query
        required: false
        schema: &id058
          type: string
          format: uuid
      - name: page
        in: query
        required: false
        schema: &id059
          type: integer
      - name: per_page
        in: query
        required: false
        schema: &id060
          type: integer
      description: Menuntut izin `production_run:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/ProductionRunRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /production-runs/start:
    post:
      tags:
      - Lapangan
      summary: 'Start a production run (timer start): creates a run row with `started_at = now()`'
      operationId: production_start_production_run_post
      description: 'Menuntut izin `production_run:create`.


        Start a production run (timer start): creates a run row with `started_at = now()`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StartRunReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductionRunRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /production-runs/{id}/stop:
    post:
      tags:
      - Lapangan
      summary: 'Stop a production run (timer stop): records output/scrap, consumes raw materials (`stock_movements(consume)`),'
      operationId: production_stop_production_run_post
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: 'Menuntut izin `production_run:update`.


        Stop a production run (timer stop): records output/scrap, consumes raw materials (`stock_movements(consume)`),
        and produces finished goods + a new lot (`stock_movements(produce)`).'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StopRunReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductionRunRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /production-runs/{run_id}/consumption:
    get:
      tags:
      - Lapangan
      summary: GET /production-runs/{run_id}/consumption
      operationId: production_list_production_consumption_get
      parameters:
      - name: run_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `production_run:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/ProductionConsumptionRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /qa/analytics:
    get:
      tags:
      - Lapangan
      summary: GET /qa/analytics
      operationId: qa_analytics_get
      description: Menuntut izin `qa:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  bugs_total: {}
                  by_severity: {}
                  by_status: {}
                  open_vs_resolved: {}
                  test_cases_total: {}
                  uat_pass_rate: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /qa/bugs:
    get:
      tags:
      - Lapangan
      summary: GET /qa/bugs
      operationId: qa_list_bugs_get
      parameters:
      - name: project_id
        in: query
        required: false
        schema: &id033
          type: string
          format: uuid
      - name: status
        in: query
        required: false
        schema: &id034
          type: string
      - name: severity
        in: query
        required: false
        schema: &id035
          type: string
      - name: page
        in: query
        required: false
        schema: &id036
          type: integer
      - name: per_page
        in: query
        required: false
        schema: &id037
          type: integer
      description: Menuntut izin `qa:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/BugRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /qa/bugs/{id}:
    get:
      tags:
      - Lapangan
      summary: GET /qa/bugs/{id}
      operationId: qa_get_bug_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `qa:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BugRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /qa/test-cases:
    get:
      tags:
      - Lapangan
      summary: GET /qa/test-cases
      operationId: qa_list_test_cases_get
      parameters:
      - name: project_id
        in: query
        required: false
        schema: &id069
          type: string
          format: uuid
      - name: page
        in: query
        required: false
        schema: &id070
          type: integer
      - name: per_page
        in: query
        required: false
        schema: &id071
          type: integer
      description: Menuntut izin `qa:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/TestCaseRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /qa/test-cases/{id}:
    get:
      tags:
      - Lapangan
      summary: GET /qa/test-cases/{id}
      operationId: qa_get_test_case_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `qa:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TestCaseRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /qa/test-cases/{id}/uat:
    get:
      tags:
      - Lapangan
      summary: GET /qa/test-cases/{id}/uat
      operationId: qa_list_uat_runs_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `qa:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/UatRunRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /stock-opnames:
    get:
      tags:
      - Lapangan
      summary: GET /stock-opnames
      operationId: stock_opname_list_get
      description: Menuntut izin `stock_opname:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/OpnameRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    post:
      tags:
      - Lapangan
      summary: POST /stock-opnames
      operationId: stock_opname_create_post
      description: Menuntut izin `stock_opname:create`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  id: {}
                  code: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /stock-opnames/{id}:
    get:
      tags:
      - Lapangan
      summary: GET /stock-opnames/{id}
      operationId: stock_opname_get_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `stock_opname:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/OpnameLineRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /stock-opnames/{id}/post:
    post:
      tags:
      - Lapangan
      summary: POST /stock-opnames/{id}/post
      operationId: stock_opname_post_post
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `stock_opname:update`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  adjusted_items: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /trip-stops/{id}/arrive:
    post:
      tags:
      - Lapangan
      summary: 'Mark a stop arrived: status=''arrived'', arrived_at=now() (idempotent on already-arrived)'
      operationId: fleet_arrive_trip_stop_post
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: 'Menuntut izin `trip:update`.


        Mark a stop arrived: status=''arrived'', arrived_at=now() (idempotent on already-arrived).'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TripStopRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /trip-stops/{id}/deliver:
    post:
      tags:
      - Lapangan
      summary: 'Deliver a stop: set status=''delivered'' + arrived_at, optionally record proof, and sync
        any Delivery Orders lin'
      operationId: fleet_deliver_trip_stop_post
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: 'Menuntut izin `trip:update`.


        Deliver a stop: set status=''delivered'' + arrived_at, optionally record proof, and sync any Delivery
        Orders linked to this stop to ''delivered'' (full delivery). Points are NO LONGER awarded per-stop
        here; driver/helper points are computed and credited atomically at trip completion (`complete_trip`).'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DeliverTripStopReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  stop: {}
                  do_lines_delivered: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /trip-stops/{id}/proof:
    get:
      tags:
      - Lapangan
      summary: GET /trip-stops/{id}/proof — stream the stored delivery proof photo (authed, `trip:read`)
      operationId: fleet_get_trip_stop_proof_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: 'Menuntut izin `trip:read`.


        GET /trip-stops/{id}/proof — stream the stored delivery proof photo (authed, `trip:read`). Content-type
        inferred from the file extension; served inline so the web/office side can render it.'
      responses:
        '200':
          description: Berkas
          content:
            image/*:
              schema:
                type: string
                format: binary
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    post:
      tags:
      - Lapangan
      summary: POST /trip-stops/{id}/proof — upload a delivery proof photo (multipart field `file`), store
        it the same way as
      operationId: fleet_upload_trip_stop_proof_post
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: 'Menuntut izin `trip:update`.


        POST /trip-stops/{id}/proof — upload a delivery proof photo (multipart field `file`), store it
        the same way as handlers::files, set trip_stops.proof_photo_url for the stop, and return { "proof_photo_url":
        "<url>" }.'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  proof_photo_url: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /trips:
    get:
      tags:
      - Lapangan
      summary: GET /trips
      operationId: fleet_list_trips_get
      parameters:
      - name: status
        in: query
        required: false
        schema: &id072
          type: string
      - name: vehicle_id
        in: query
        required: false
        schema: &id073
          type: string
          format: uuid
      - name: driver_employee_id
        in: query
        required: false
        schema: &id074
          type: string
          format: uuid
      - name: scope
        in: query
        required: false
        schema: &id075
          type: string
      - name: page
        in: query
        required: false
        schema: &id076
          type: integer
      - name: per_page
        in: query
        required: false
        schema: &id077
          type: integer
      description: Menuntut izin `trip:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/TripListRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    post:
      tags:
      - Lapangan
      summary: POST /trips
      operationId: fleet_create_trip_post
      description: Menuntut izin `trip:create`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateTripReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TripRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /trips/{id}:
    delete:
      tags:
      - Lapangan
      summary: DELETE /trips/{id}
      operationId: fleet_delete_trip_delete
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `trip:delete`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    get:
      tags:
      - Lapangan
      summary: GET /trips/{id}
      operationId: fleet_get_trip_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `trip:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TripRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    put:
      tags:
      - Lapangan
      summary: PUT /trips/{id}
      operationId: fleet_update_trip_put
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `trip:update`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateTripReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TripRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /trips/{id}/complete:
    post:
      tags:
      - Lapangan
      summary: POST /trips/{id}/complete — finalize a trip and award driver/helper points
      operationId: fleet_complete_trip_post
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: 'Menuntut izin `trip:update`.


        POST /trips/{id}/complete — finalize a trip and award driver/helper points. Locks the trip, guards
        against re-completion, sets status=''completed'' + `ended_at`, resolves DOs (delivered→completed,
        otherwise→returned), then computes earned points per `wh_settings.point_mode` and writes up to
        two `point_ledger` earn rows split via `point_driver_share`/`point_helper_share`. Idempotent:
        a trip already completed, or one with an existing "trip completed" ledger row, is not re-awarded.
        Returns `{ status, points_earned, driver_points, helper_points }`.'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  points_earned:
                    type: number
                  driver_points:
                    type: number
                  helper_points:
                    type: number
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /trips/{id}/start:
    post:
      tags:
      - Lapangan
      summary: POST /trips/{id}/start — move a planned/loading trip to 'en_route'
      operationId: fleet_start_trip_post
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: 'Menuntut izin `trip:update`.


        POST /trips/{id}/start — move a planned/loading trip to ''en_route''. Sets `started_at` (idempotent
        COALESCE) and promotes the trip''s Delivery Orders from ''assigned''/''loaded'' to ''in_transit''.
        Tenant-scoped via RLS.'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TripRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /trips/{trip_id}/checkpoints:
    get:
      tags:
      - Lapangan
      summary: GET /trips/{trip_id}/checkpoints
      operationId: field_ops_list_checkpoints_get
      parameters:
      - name: trip_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: 'Menuntut izin `checkpoint:read`.


        GET /trips/{trip_id}/checkpoints'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/CheckpointRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    post:
      tags:
      - Lapangan
      summary: POST /trips/{trip_id}/checkpoints — multipart `{file(photo), lat, lng, kind, note}`
      operationId: field_ops_create_checkpoint_post
      parameters:
      - name: trip_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: 'Menuntut izin `checkpoint:create`.


        POST /trips/{trip_id}/checkpoints — multipart `{file(photo), lat, lng, kind, note}`. Stores the
        photo, inserts a trip_checkpoints row (by_employee_id from actor_ctx).'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CheckpointRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /trips/{trip_id}/delivery-orders:
    get:
      tags:
      - Lapangan
      summary: GET /trips/{trip_id}/delivery-orders — list DOs attached to a trip
      operationId: distribution_list_trip_delivery_orders_get
      parameters:
      - name: trip_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: 'Menuntut izin `trip:read`.


        GET /trips/{trip_id}/delivery-orders — list DOs attached to a trip.'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/DeliveryOrderRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    post:
      tags:
      - Lapangan
      summary: POST /trips/{trip_id}/delivery-orders — attach DOs to a trip
      operationId: distribution_attach_delivery_orders_post
      parameters:
      - name: trip_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: 'Menuntut izin `trip:update`.


        POST /trips/{trip_id}/delivery-orders — attach DOs to a trip. For each DO (must be tenant-scoped
        and currently created/assigned): set its trip_id, flip status→''assigned'', create a `trip_stops`
        dropoff row, and link it via `trip_stop_dos`. Then run a SOFT capacity check (total DO weight
        on the trip vs the vehicle capacity_kg) — over-capacity is reported but does not block.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AttachDeliveryOrdersReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  attached: {}
                  total_kg: {}
                  capacity_kg: {}
                  over_capacity: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /trips/{trip_id}/gps-pings:
    get:
      tags:
      - Lapangan
      summary: List GPS pings for a trip (most recent first)
      operationId: fleet_list_gps_pings_get
      parameters:
      - name: trip_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: 'Menuntut izin `trip:read`.


        List GPS pings for a trip (most recent first).'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/GpsPingRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /trips/{trip_id}/handovers:
    get:
      tags:
      - Lapangan
      summary: GET /trips/{trip_id}/handovers
      operationId: field_ops_list_handovers_get
      parameters:
      - name: trip_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: 'Menuntut izin `handover:read`.


        GET /trips/{trip_id}/handovers'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/HandoverRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    post:
      tags:
      - Lapangan
      summary: POST /trips/{trip_id}/handovers — multipart `{file?(photo/signature), phase, do_id?, counterparty_name?,
        tie_d
      operationId: field_ops_create_handover_post
      parameters:
      - name: trip_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: 'Menuntut izin `handover:create`.


        POST /trips/{trip_id}/handovers — multipart `{file?(photo/signature), phase, do_id?, counterparty_name?,
        tie_down_ok?, note?}`. Optional photo stored into photo_url; inserts cargo_handovers (by_employee_id
        from actor).'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HandoverRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /trips/{trip_id}/stops:
    get:
      tags:
      - Lapangan
      summary: GET /trips/{trip_id}/stops
      operationId: fleet_list_trip_stops_get
      parameters:
      - name: trip_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `trip:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/TripStopRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    post:
      tags:
      - Lapangan
      summary: POST /trips/{trip_id}/stops
      operationId: fleet_create_trip_stop_post
      parameters:
      - name: trip_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `trip:update`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateTripStopReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TripStopRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /vehicles:
    get:
      tags:
      - Lapangan
      summary: GET /vehicles
      operationId: fleet_list_vehicles_get
      parameters:
      - name: status
        in: query
        required: false
        schema: &id078
          type: string
      - name: page
        in: query
        required: false
        schema: &id079
          type: integer
      - name: per_page
        in: query
        required: false
        schema: &id080
          type: integer
      description: Menuntut izin `vehicle:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/VehicleRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    post:
      tags:
      - Lapangan
      summary: POST /vehicles
      operationId: fleet_create_vehicle_post
      description: Menuntut izin `vehicle:create`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateVehicleReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VehicleRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /vehicles/{id}:
    delete:
      tags:
      - Lapangan
      summary: DELETE /vehicles/{id}
      operationId: fleet_delete_vehicle_delete
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `vehicle:delete`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    get:
      tags:
      - Lapangan
      summary: GET /vehicles/{id}
      operationId: fleet_get_vehicle_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `vehicle:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VehicleRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    put:
      tags:
      - Lapangan
      summary: PUT /vehicles/{id}
      operationId: fleet_update_vehicle_put
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `vehicle:update`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateVehicleReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VehicleRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /vehicles/{vehicle_id}/maintenance:
    post:
      tags:
      - Lapangan
      summary: POST /vehicles/{vehicle_id}/maintenance — JSON `{kind, odometer_km?, description?, cost_idr?}`
      operationId: field_ops_create_maintenance_post
      parameters:
      - name: vehicle_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: 'Menuntut izin `vehicle_maintenance:create`.


        POST /vehicles/{vehicle_id}/maintenance — JSON `{kind, odometer_km?, description?, cost_idr?}`.
        reported_by from actor.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateMaintenanceReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MaintenanceRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /branches:
    get:
      tags:
      - Direktori
      summary: GET /branches
      operationId: org_list_branches_get
      parameters:
      - name: page
        in: query
        required: false
        schema: *id001
      - name: per_page
        in: query
        required: false
        schema: *id002
      description: Menuntut izin `branch:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/BranchRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    post:
      tags:
      - Direktori
      summary: POST /branches
      operationId: org_create_branch_post
      description: Menuntut izin `branch:create`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BranchCreateReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BranchRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /branches/{id}:
    delete:
      tags:
      - Direktori
      summary: DELETE /branches/{id}
      operationId: org_delete_branch_delete
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `branch:delete`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    get:
      tags:
      - Direktori
      summary: GET /branches/{id}
      operationId: org_get_branch_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: 'Menuntut izin `branch:read`.


        GET /branches/{id}'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  id: {}
                  name: {}
                  kind:
                    type: string
                  address: {}
                  timezone: {}
                  created_at: {}
                  summary: {}
                  by_group: {}
                  employees:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    put:
      tags:
      - Direktori
      summary: PUT /branches/{id}
      operationId: org_update_branch_put
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `branch:update`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BranchUpdateReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BranchRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /departments:
    get:
      tags:
      - Direktori
      summary: GET /departments
      operationId: org_list_departments_get
      parameters:
      - name: page
        in: query
        required: false
        schema: *id001
      - name: per_page
        in: query
        required: false
        schema: *id002
      description: Menuntut izin `department:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/DepartmentListRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    post:
      tags:
      - Direktori
      summary: POST /departments
      operationId: org_create_department_post
      description: Menuntut izin `department:create`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DepartmentCreateReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DepartmentRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /departments/{id}:
    delete:
      tags:
      - Direktori
      summary: DELETE /departments/{id}
      operationId: org_delete_department_delete
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `department:delete`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    get:
      tags:
      - Direktori
      summary: GET /departments/{id}
      operationId: org_get_department_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: 'Menuntut izin `department:read`.


        GET /departments/{id}'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  id: {}
                  name: {}
                  kind:
                    type: string
                  parent: {}
                  created_at: {}
                  extra: {}
                  summary: {}
                  by_group: {}
                  employees:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    put:
      tags:
      - Direktori
      summary: PUT /departments/{id}
      operationId: org_update_department_put
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `department:update`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DepartmentUpdateReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DepartmentRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /employees:
    get:
      tags:
      - Direktori
      summary: GET /employees
      operationId: employees_list_get
      parameters:
      - name: page
        in: query
        required: false
        schema: *id001
      - name: per_page
        in: query
        required: false
        schema: *id002
      description: Menuntut izin `employee:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/EmployeeRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    post:
      tags:
      - Direktori
      summary: POST /employees
      operationId: employees_create_post
      description: Menuntut izin `employee:create`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmployeeRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /employees/{employee_id}:
    get:
      tags:
      - Direktori
      summary: GET /employees/{id} — detail lengkap satu karyawan + relasi org, akun, dan ringkasan cuti
        / kehadiran / timesh
      operationId: employees_get_one_get
      parameters:
      - name: employee_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: 'Menuntut izin `employee:read, payroll:read`.


        GET /employees/{id} — detail lengkap satu karyawan + relasi org, akun, dan ringkasan cuti / kehadiran
        / timesheet (untuk panel detail kaya di FE).'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  id: {}
                  code: {}
                  name: {}
                  email: {}
                  phone: {}
                  gender: {}
                  employment_status: {}
                  hire_date: {}
                  is_active: {}
                  tax_status: {}
                  has_npwp: {}
                  npwp: {}
                  photo: {}
                  created_at: {}
                  updated_at: {}
                  department: {}
                  team: {}
                  position: {}
                  branch: {}
                  manager: {}
                  account: {}
                  nik: {}
                  birth_date: {}
                  birth_place: {}
                  address: {}
                  marital_status: {}
                  religion: {}
                  education: {}
                  blood_type: {}
                  emergency_name: {}
                  emergency_phone: {}
                  emergency_relation: {}
                  bank_name: {}
                  bank_account_no: {}
                  bank_account_name: {}
                  bpjs_kes_no: {}
                  bpjs_tk_no: {}
                  contract_type: {}
                  contract_number: {}
                  contract_start: {}
                  contract_end: {}
                  termination_date: {}
                  termination_reason: {}
                  branch_id: {}
                  department_id: {}
                  team_id: {}
                  position_id: {}
                  manager_id: {}
                  leave_balances:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  attendance_90d: {}
                  timesheet_trend:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  salary: {}
                  payroll_components:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  projects_owned:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  projects_member:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  tasks:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  kpis:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  goals:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  reviews:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  objectives:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  leave_requests:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  reimbursements:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  overtime:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  timesheet_recent:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  payslips:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  activity:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  counts: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /employees/{employee_id}/payroll-components:
    post:
      tags:
      - Direktori
      summary: POST /employees/{employee_id}/payroll-components
      operationId: payroll_create_component_post
      parameters:
      - name: employee_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `payroll:create`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateComponentReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ComponentRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /employees/{employee_id}/payroll-components/{id}:
    delete:
      tags:
      - Direktori
      summary: DELETE /employees/{employee_id}/payroll-components/{id}
      operationId: payroll_delete_component_delete
      parameters:
      - name: employee_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `payroll:delete`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /employees/{employee_id}/salary-structures:
    post:
      tags:
      - Direktori
      summary: POST /employees/{employee_id}/salary-structures
      operationId: payroll_set_salary_post
      parameters:
      - name: employee_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `payroll:create`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetSalaryReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SalaryStructureRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /employees/{id}/app-pin:
    get:
      tags:
      - Direktori
      summary: 'GET /employees/{id}/app-pin : keadaan PIN, tanpa pernah menampilkan nilainya'
      operationId: employee_auth_status_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: 'Menuntut izin `employee:read`.


        GET /employees/{id}/app-pin : keadaan PIN, tanpa pernah menampilkan nilainya.


        Bentuk balasan dibaca tangan dari kode, bukan dari struct.'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  punya_pin:
                    type: boolean
                  sisa_percobaan:
                    type: integer
                  terkunci_sampai:
                    type: string
                    format: date-time
                  harus_ganti:
                    type: boolean
                  diperbarui:
                    type: string
                    format: date-time
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /positions:
    get:
      tags:
      - Direktori
      summary: GET /positions
      operationId: org_list_positions_get
      parameters:
      - name: page
        in: query
        required: false
        schema: *id001
      - name: per_page
        in: query
        required: false
        schema: *id002
      description: Menuntut izin `position:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/PositionRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    post:
      tags:
      - Direktori
      summary: POST /positions
      operationId: org_create_position_post
      description: Menuntut izin `position:create`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PositionCreateReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PositionRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /positions/{id}:
    delete:
      tags:
      - Direktori
      summary: DELETE /positions/{id}
      operationId: org_delete_position_delete
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `position:delete`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    get:
      tags:
      - Direktori
      summary: GET /positions/{id}
      operationId: org_get_position_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: 'Menuntut izin `position:read`.


        GET /positions/{id}'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  id: {}
                  name: {}
                  kind:
                    type: string
                  created_at: {}
                  extra: {}
                  summary: {}
                  by_group: {}
                  employees:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    put:
      tags:
      - Direktori
      summary: PUT /positions/{id}
      operationId: org_update_position_put
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `position:update`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PositionUpdateReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PositionRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /teams:
    get:
      tags:
      - Direktori
      summary: GET /teams
      operationId: org_list_teams_get
      parameters:
      - name: page
        in: query
        required: false
        schema: *id001
      - name: per_page
        in: query
        required: false
        schema: *id002
      description: Menuntut izin `team:read`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/TeamRow'
                  page:
                    type: integer
                  per_page:
                    type: integer
                  total:
                    type: integer
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    post:
      tags:
      - Direktori
      summary: POST /teams
      operationId: org_create_team_post
      description: Menuntut izin `team:create`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TeamCreateReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TeamRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
  /teams/{id}:
    delete:
      tags:
      - Direktori
      summary: DELETE /teams/{id}
      operationId: org_delete_team_delete
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `team:delete`.
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted: {}
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    get:
      tags:
      - Direktori
      summary: GET /teams/{id}
      operationId: org_get_team_get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: 'Menuntut izin `team:read`.


        GET /teams/{id}'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                type: object
                properties:
                  id: {}
                  name: {}
                  kind:
                    type: string
                  department: {}
                  created_at: {}
                  summary: {}
                  by_group: {}
                  employees:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
    put:
      tags:
      - Direktori
      summary: PUT /teams/{id}
      operationId: org_update_team_put
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      description: Menuntut izin `team:update`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TeamUpdateReq'
      responses:
        '200':
          description: Berhasil
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TeamRow'
        '400':
          $ref: '#/components/responses/Galat'
        '401':
          $ref: '#/components/responses/Galat'
        '403':
          $ref: '#/components/responses/Galat'
        '404':
          $ref: '#/components/responses/Galat'
components:
  securitySchemes:
    bearer:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Access token 30 menit, refresh token 14 hari.
  responses:
    Galat:
      description: Galat
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Galat'
  schemas:
    AckBriefingReq:
      type: object
      properties:
        lat:
          type: number
        lng:
          type: number
    ActReq:
      type: object
      properties:
        approve:
          type: boolean
        comment:
          type: string
      required:
      - approve
    AnnouncementListQuery:
      type: object
      properties:
        is_active: *id008
        page: *id009
        per_page: *id010
    AnnouncementRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        title:
          type: string
        content:
          type: string
        target_type:
          type: string
        is_active:
          type: boolean
        author_membership_id:
          type: string
          format: uuid
        created_at:
          type: string
          format: date-time
      required:
      - id
      - title
      - content
      - target_type
      - is_active
      - created_at
    ApprovalRequest:
      type: object
      properties:
        id:
          type: string
          format: uuid
        resource_type:
          type: string
        resource_id:
          type: string
          format: uuid
        requester_membership_id:
          type: string
          format: uuid
        amount:
          type: integer
        status:
          type: string
        current_step:
          type: integer
      required:
      - id
      - resource_type
      - resource_id
      - requester_membership_id
      - status
      - current_step
    ArticleListQuery:
      type: object
      properties:
        space_id: *id011
        kind: *id012
        status: *id013
        page: *id014
        per_page: *id015
    ArticleRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        space_id:
          type: string
          format: uuid
        title:
          type: string
        body:
          type: string
        kind:
          type: string
        status:
          type: string
        visibility:
          type: string
        author_membership_id:
          type: string
          format: uuid
        version:
          type: integer
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
      - id
      - title
      - body
      - kind
      - status
      - visibility
      - version
      - created_at
      - updated_at
    AttachDeliveryOrdersReq:
      type: object
      properties:
        do_ids:
          type: array
          items:
            type: string
            format: uuid
      required:
      - do_ids
    AttendanceListRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        employee_id:
          type: string
          format: uuid
        employee_name:
          type: string
        work_date:
          type: string
          format: date
        check_in:
          type: string
          format: date-time
        check_out:
          type: string
          format: date-time
        status:
          type: string
        latitude:
          type: number
        longitude:
          type: number
        note:
          type: string
        shift_id:
          type: string
          format: uuid
        method:
          type: string
        photo_file_id:
          type: string
          format: uuid
        out_of_geofence:
          type: boolean
      required:
      - id
      - employee_id
      - work_date
      - out_of_geofence
    AttendanceRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        employee_id:
          type: string
          format: uuid
        work_date:
          type: string
          format: date
        check_in:
          type: string
          format: date-time
        check_out:
          type: string
          format: date-time
        status:
          type: string
        latitude:
          type: number
        longitude:
          type: number
        note:
          type: string
        shift_id:
          type: string
          format: uuid
        method:
          type: string
        photo_file_id:
          type: string
          format: uuid
        out_of_geofence:
          type: boolean
      required:
      - id
      - employee_id
      - work_date
      - out_of_geofence
    BalanceQuery:
      type: object
      properties:
        employee_id: *id016
        leave_type_id: *id017
        year: *id018
    BoardTaskRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        title:
          type: string
        status_id:
          type: string
          format: uuid
        column_name:
          type: string
        priority:
          type: string
        assignee_employee_id:
          type: string
          format: uuid
      required:
      - id
      - title
      - priority
    BranchCreateReq:
      type: object
      properties:
        name:
          type: string
        address:
          type: string
        timezone:
          type: string
      required:
      - name
    BranchRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        address:
          type: string
        timezone:
          type: string
        created_at:
          type: string
          format: date-time
      required:
      - id
      - name
      - timezone
      - created_at
    BranchUpdateReq:
      type: object
      properties:
        name:
          type: string
        address:
          type: string
        timezone:
          type: string
    BriefingAckRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        briefing_id:
          type: string
          format: uuid
        employee_id:
          type: string
          format: uuid
        lat:
          type: number
        lng:
          type: number
        at:
          type: string
          format: date-time
      required:
      - id
      - briefing_id
      - employee_id
      - at
    BriefingRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        trip_id:
          type: string
          format: uuid
        supervisor_employee_id:
          type: string
          format: uuid
        title:
          type: string
        body:
          type: string
        attachment_url:
          type: string
        for_date:
          type: string
          format: date
        created_at:
          type: string
          format: date-time
      required:
      - id
      - title
      - created_at
    BugRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        project_id:
          type: string
          format: uuid
        title:
          type: string
        description:
          type: string
        severity:
          type: string
        status:
          type: string
        reporter_membership_id:
          type: string
          format: uuid
        assignee_employee_id:
          type: string
          format: uuid
        created_at:
          type: string
          format: date-time
        resolved_at:
          type: string
          format: date-time
      required:
      - id
      - title
      - severity
      - status
      - created_at
    CheckInReq:
      type: object
      properties:
        employee_id:
          type: string
          format: uuid
        shift_id:
          type: string
          format: uuid
        latitude:
          type: number
        longitude:
          type: number
        note:
          type: string
        method:
          type: string
        photo_file_id:
          type: string
          format: uuid
    CheckOutReq:
      type: object
      properties:
        employee_id:
          type: string
          format: uuid
        latitude:
          type: number
        longitude:
          type: number
        note:
          type: string
    ChecklistItemRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        task_id:
          type: string
          format: uuid
        label:
          type: string
        is_done:
          type: boolean
        position:
          type: integer
      required:
      - id
      - task_id
      - label
      - is_done
      - position
    CheckpointRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        trip_id:
          type: string
          format: uuid
        trip_stop_id:
          type: string
          format: uuid
        kind:
          type: string
        photo_url:
          type: string
        lat:
          type: number
        lng:
          type: number
        note:
          type: string
        by_employee_id:
          type: string
          format: uuid
        at:
          type: string
          format: date-time
      required:
      - id
      - trip_id
      - kind
      - at
    CodeReq:
      type: object
      properties:
        code:
          type: string
      required:
      - code
    ComponentRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        employee_id:
          type: string
          format: uuid
        name:
          type: string
        kind:
          type: string
        amount:
          type: integer
        created_at:
          type: string
          format: date-time
      required:
      - id
      - employee_id
      - name
      - kind
      - amount
      - created_at
    ConnectReq:
      type: object
      properties:
        client_id:
          type: string
        client_secret:
          type: string
        db_id:
          type: string
        faktur_mode:
          type: boolean
      required:
      - client_id
      - client_secret
      - db_id
      - faktur_mode
    ConsumptionReq:
      type: object
      properties:
        raw_item_id:
          type: string
          format: uuid
        lot_id:
          type: string
          format: uuid
        qty:
          type: integer
        location_id:
          type: string
          format: uuid
      required:
      - raw_item_id
      - qty
    ContactQuery:
      type: object
      properties:
        q: *id019
        limit: *id020
    ConvertReq:
      type: object
      properties:
        period:
          type: string
      required:
      - period
    CreateBriefingReq:
      type: object
      properties:
        trip_id:
          type: string
          format: uuid
        title:
          type: string
        body:
          type: string
        for_date:
          type: string
          format: date
    CreateComponentReq:
      type: object
      properties:
        name:
          type: string
        kind:
          type: string
        amount:
          type: integer
      required:
      - name
      - kind
      - amount
    CreateIncidentReq:
      type: object
      properties:
        trip_id:
          type: string
          format: uuid
        kind:
          type: string
        severity:
          type: string
        description:
          type: string
    CreateLedgerReq:
      type: object
      properties:
        employee_id:
          type: string
          format: uuid
        trip_id:
          type: string
          format: uuid
        stop_id:
          type: string
          format: uuid
        kind:
          type: string
        points:
          type: number
        reason:
          type: string
    CreateLoanReq:
      type: object
      properties:
        employee_id:
          type: string
          format: uuid
        principal:
          type: integer
        installments:
          type: integer
        start_period:
          type: string
        reason:
          type: string
      required:
      - employee_id
      - principal
      - installments
      - start_period
    CreateMaintenanceReq:
      type: object
      properties:
        kind:
          type: string
        trip_id:
          type: string
          format: uuid
        odometer_km:
          type: number
        description:
          type: string
        cost_idr:
          type: integer
    CreateReq:
      type: object
      properties:
        location_id:
          type: string
          format: uuid
        note:
          type: string
      required:
      - location_id
    CreateTripReq:
      type: object
      properties:
        vehicle_id:
          type: string
          format: uuid
        driver_employee_id:
          type: string
          format: uuid
        helper_employee_id:
          type: string
          format: uuid
        supervisor_employee_id:
          type: string
          format: uuid
        origin_location_id:
          type: string
          format: uuid
        source:
          type: string
    CreateTripStopReq:
      type: object
      properties:
        seq:
          type: integer
        so_id:
          type: string
          format: uuid
        customer_id:
          type: string
          format: uuid
        address:
          type: string
        lat:
          type: number
        lng:
          type: number
        distance_km:
          type: number
        note:
          type: string
    CreateTypeReq:
      type: object
      properties:
        name:
          type: string
    CreateVehicleReq:
      type: object
      properties:
        name:
          type: string
        plate:
          type: string
        year:
          type: integer
        capacity_kg:
          type: number
        capacity_vol:
          type: number
        status:
          type: string
        is_active:
          type: boolean
    DelayedRow:
      type: object
      properties:
        do_id:
          type: string
          format: uuid
        do_number:
          type: string
        customer_name:
          type: string
        due_at:
          type: string
          format: date-time
        days_late:
          type: integer
        status:
          type: string
        trip_id:
          type: string
          format: uuid
        driver_name:
          type: string
      required:
      - do_id
      - do_number
      - days_late
      - status
    DeliverTripStopReq:
      type: object
      properties:
        proof_photo_url:
          type: string
        note:
          type: string
        distance_km:
          type: number
    DeliveryOrderRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        do_number:
          type: string
        so_id:
          type: string
          format: uuid
        showroom_location_id:
          type: string
          format: uuid
        gudang_location_id:
          type: string
          format: uuid
        customer_id:
          type: string
          format: uuid
        ship_address:
          type: string
        ship_lat:
          type: number
        ship_lng:
          type: number
        status:
          type: string
        trip_id:
          type: string
          format: uuid
        total_weight_kg:
          type: number
        total_points:
          type: number
        priority:
          type: integer
        due_at:
          type: string
          format: date-time
        note:
          type: string
        printed_at:
          type: string
          format: date-time
        created_by_employee_id:
          type: string
          format: uuid
        created_at:
          type: string
          format: date-time
      required:
      - id
      - do_number
      - status
      - priority
      - created_at
    DepartmentCreateReq:
      type: object
      properties:
        name:
          type: string
        parent_id:
          type: string
          format: uuid
      required:
      - name
    DepartmentListRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        parent_id:
          type: string
          format: uuid
        parent_name:
          type: string
        created_at:
          type: string
          format: date-time
      required:
      - id
      - name
      - created_at
    DepartmentRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        parent_id:
          type: string
          format: uuid
        created_at:
          type: string
          format: date-time
      required:
      - id
      - name
      - created_at
    DepartmentUpdateReq:
      type: object
      properties:
        name:
          type: string
        parent_id:
          type: string
          format: uuid
    DriverReportRow:
      type: object
      properties:
        employee_id:
          type: string
          format: uuid
        name:
          type: string
        trips:
          type: integer
        deliveries:
          type: integer
        points:
          type: number
        incidents:
          type: integer
        on_time_rate:
          type: number
      required:
      - employee_id
      - trips
      - deliveries
      - points
      - incidents
    EmployeeRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        code:
          type: string
        name:
          type: string
        email:
          type: string
        phone:
          type: string
        employment_status:
          type: string
        hire_date:
          type: string
          format: date
        is_active:
          type: boolean
      required:
      - id
      - code
      - name
      - employment_status
      - is_active
    FileRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        mime:
          type: string
        size_bytes:
          type: integer
        storage_path:
          type: string
        entity:
          type: string
        entity_id:
          type: string
          format: uuid
        uploaded_by_membership_id:
          type: string
          format: uuid
        created_at:
          type: string
          format: date-time
      required:
      - id
      - name
      - mime
      - size_bytes
      - storage_path
      - created_at
    FolderQuery:
      type: object
      properties:
        folder: *id005
      required:
      - folder
    GantiReq:
      type: object
      properties:
        pin_lama:
          type: string
        pin_baru:
          type: string
      required:
      - pin_baru
    GatewayReq:
      type: object
      properties:
        credential_kind:
          type: string
        credential:
          type: string
        employee_code:
          type: string
        method:
          type: string
    GpsPingRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        trip_id:
          type: string
          format: uuid
        vehicle_id:
          type: string
          format: uuid
        lat:
          type: number
        lng:
          type: number
        speed:
          type: number
        recorded_at:
          type: string
          format: date-time
        source:
          type: string
      required:
      - id
      - lat
      - lng
      - recorded_at
      - source
    HandoverRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        trip_id:
          type: string
          format: uuid
        trip_stop_id:
          type: string
          format: uuid
        do_id:
          type: string
          format: uuid
        phase:
          type: string
        by_employee_id:
          type: string
          format: uuid
        counterparty_name:
          type: string
        signature_url:
          type: string
        photo_url:
          type: string
        tie_down_ok:
          type: boolean
        note:
          type: string
        at:
          type: string
          format: date-time
      required:
      - id
      - trip_id
      - phase
      - at
    InboxRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        resource_type:
          type: string
        resource_id:
          type: string
          format: uuid
        requester_membership_id:
          type: string
          format: uuid
        amount:
          type: integer
        status:
          type: string
        current_step:
          type: integer
        created_at:
          type: string
          format: date-time
        requester_name:
          type: string
        requester_code:
          type: string
        title:
          type: string
        note:
          type: string
        start_date:
          type: string
          format: date
        end_date:
          type: string
          format: date
        receipt_url:
          type: string
        step_roles:
          type: string
      required:
      - id
      - resource_type
      - resource_id
      - requester_membership_id
      - status
      - current_step
      - created_at
    IncidentRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        trip_id:
          type: string
          format: uuid
        reported_by_employee_id:
          type: string
          format: uuid
        kind:
          type: string
        severity:
          type: string
        status:
          type: string
        description:
          type: string
        resolution:
          type: string
        at:
          type: string
          format: date-time
      required:
      - id
      - kind
      - severity
      - status
      - at
    LeaderboardQuery:
      type: object
      properties:
        from: *id021
        to: *id022
        limit: *id023
    LeaderboardRow:
      type: object
      properties:
        employee_id:
          type: string
          format: uuid
        total_points:
          type: number
        earn_points:
          type: number
        deduct_points:
          type: number
        entries:
          type: integer
      required:
      - employee_id
      - total_points
      - earn_points
      - deduct_points
      - entries
    LeaveBalanceRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        employee_id:
          type: string
          format: uuid
        leave_type_id:
          type: string
          format: uuid
        year:
          type: integer
        entitled_days:
          type: integer
        used_days:
          type: integer
      required:
      - id
      - employee_id
      - leave_type_id
      - year
      - entitled_days
      - used_days
    LeaveRequestListRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        employee_id:
          type: string
          format: uuid
        employee_name:
          type: string
        leave_type_id:
          type: string
          format: uuid
        leave_type_name:
          type: string
        start_date:
          type: string
          format: date
        end_date:
          type: string
          format: date
        days:
          type: integer
        reason:
          type: string
        approval_request_id:
          type: string
          format: uuid
        approval_status:
          type: string
      required:
      - id
      - employee_id
      - leave_type_id
      - start_date
      - end_date
      - days
    LeaveRequestRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        employee_id:
          type: string
          format: uuid
        leave_type_id:
          type: string
          format: uuid
        start_date:
          type: string
          format: date
        end_date:
          type: string
          format: date
        days:
          type: integer
        reason:
          type: string
        approval_request_id:
          type: string
          format: uuid
        approval_status:
          type: string
      required:
      - id
      - employee_id
      - leave_type_id
      - start_date
      - end_date
      - days
    LeaveTypeReq:
      type: object
      properties:
        name:
          type: string
        quota_days:
          type: integer
        is_paid:
          type: boolean
    LeaveTypeRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        quota_days:
          type: integer
        is_paid:
          type: boolean
      required:
      - id
      - name
      - quota_days
      - is_paid
    ListAttendanceQuery:
      type: object
      properties:
        employee_id: *id024
        from: *id025
        to: *id026
        page: *id027
        per_page: *id028
    ListBriefingsQuery:
      type: object
      properties:
        for_date: *id029
        trip_id: *id030
        page: *id031
        per_page: *id032
    ListBugsQuery:
      type: object
      properties:
        project_id: *id033
        status: *id034
        severity: *id035
        page: *id036
        per_page: *id037
    ListIncidentsQuery:
      type: object
      properties:
        status: *id038
        trip_id: *id039
        page: *id040
        per_page: *id041
    ListLeaveQuery:
      type: object
      properties:
        employee_id: *id042
        page: *id043
        per_page: *id044
    ListLedgerQuery:
      type: object
      properties:
        employee_id: *id045
        trip_id: *id046
        kind: *id047
        page: *id048
        per_page: *id049
    ListOvertimeQuery:
      type: object
      properties:
        employee_id: *id050
        from: *id051
        to: *id052
        page: *id053
        per_page: *id054
    ListProjectsQuery:
      type: object
      properties:
        page: *id055
        per_page: *id056
        status: *id057
    ListQuery:
      type: object
      properties:
        page: *id001
        per_page: *id002
    ListRunsQuery:
      type: object
      properties:
        batch_id: *id058
        page: *id059
        per_page: *id060
    ListSprintsQuery:
      type: object
      properties:
        project_id: *id061
        page: *id062
        per_page: *id063
    ListTargetsQuery:
      type: object
      properties:
        period: *id064
    ListTasksQuery:
      type: object
      properties:
        status_id: *id065
        assignee_employee_id: *id066
        page: *id067
        per_page: *id068
    ListTestCasesQuery:
      type: object
      properties:
        project_id: *id069
        page: *id070
        per_page: *id071
    ListTripsQuery:
      type: object
      properties:
        status: *id072
        vehicle_id: *id073
        driver_employee_id: *id074
        scope: *id075
        page: *id076
        per_page: *id077
    ListVehiclesQuery:
      type: object
      properties:
        status: *id078
        page: *id079
        per_page: *id080
    LoanQuery:
      type: object
      properties:
        employee_id: *id081
        status: *id082
    LoanRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        employee_id:
          type: string
          format: uuid
        employee_name:
          type: string
        principal:
          type: integer
        installments:
          type: integer
        installment_amount:
          type: integer
        remaining:
          type: integer
        start_period:
          type: string
        reason:
          type: string
        status:
          type: string
        paid_count:
          type: integer
      required:
      - id
      - employee_id
      - principal
      - installments
      - installment_amount
      - remaining
      - start_period
      - status
      - paid_count
    LoginReq:
      type: object
      properties:
        email:
          type: string
        password:
          type: string
        totp_code:
          type: string
      required:
      - email
      - password
    MaintenanceRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        vehicle_id:
          type: string
          format: uuid
        trip_id:
          type: string
          format: uuid
        reported_by_employee_id:
          type: string
          format: uuid
        kind:
          type: string
        odometer_km:
          type: number
        status:
          type: string
        description:
          type: string
        photo_url:
          type: string
        cost_idr:
          type: integer
        at:
          type: string
          format: date-time
      required:
      - id
      - vehicle_id
      - kind
      - status
      - at
    MapAccountReq:
      type: object
      properties:
        email_address:
          type: string
        password:
          type: string
      required:
      - email_address
    MasukReq:
      type: object
      properties:
        employee_code:
          type: string
        pin:
          type: string
      required:
      - employee_code
      - pin
    MessageActionReq:
      type: object
      properties:
        seen:
          type: boolean
        flagged:
          type: boolean
        move_to:
          type: string
    MessagesQuery:
      type: object
      properties:
        folder: *id083
        page: *id084
        per_page: *id085
    MonitorLeaderRow:
      type: object
      properties:
        employee_id:
          type: string
          format: uuid
        name:
          type: string
        points:
          type: number
      required:
      - employee_id
      - points
    MoveTaskReq:
      type: object
      properties:
        status_id:
          type: string
          format: uuid
        position:
          type: integer
    NotifRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        kind:
          type: string
        title:
          type: string
        body:
          type: string
        link:
          type: string
        is_read:
          type: boolean
        created_at:
          type: string
          format: date-time
      required:
      - id
      - kind
      - title
      - is_read
      - created_at
    OpnameLineRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        item_id:
          type: string
          format: uuid
        item_name:
          type: string
        sku:
          type: string
        uom:
          type: string
        system_qty:
          type: integer
        counted_qty:
          type: integer
        variance:
          type: integer
        note:
          type: string
      required:
      - id
      - item_id
      - system_qty
      - counted_qty
      - variance
    OpnameRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        location_id:
          type: string
          format: uuid
        location_name:
          type: string
        code:
          type: string
        status:
          type: string
        note:
          type: string
        line_count:
          type: integer
        posted_at:
          type: string
          format: date-time
        created_at:
          type: string
          format: date-time
      required:
      - id
      - location_id
      - status
      - line_count
      - created_at
    OvertimeRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        employee_id:
          type: string
          format: uuid
        work_date:
          type: string
          format: date
        minutes:
          type: integer
        reason:
          type: string
        approval_request_id:
          type: string
          format: uuid
        approval_status:
          type: string
      required:
      - id
      - employee_id
      - work_date
      - minutes
    PageQuery:
      type: object
      properties:
        page: *id003
        per_page: *id004
    PasswordReq:
      type: object
      properties:
        current_password:
          type: string
        new_password:
          type: string
      required:
      - current_password
      - new_password
    PointLedgerRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        employee_id:
          type: string
          format: uuid
        trip_id:
          type: string
          format: uuid
        stop_id:
          type: string
          format: uuid
        kind:
          type: string
        points:
          type: number
        reason:
          type: string
        rule_ref:
          type: string
          format: uuid
        by_employee_id:
          type: string
          format: uuid
        at:
          type: string
          format: date-time
      required:
      - id
      - employee_id
      - kind
      - points
      - at
    PointTargetRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        employee_id:
          type: string
          format: uuid
        period_kind:
          type: string
        period:
          type: string
        target_points:
          type: number
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
      - id
      - period_kind
      - period
      - target_points
      - created_at
      - updated_at
    PollOptionRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        poll_id:
          type: string
          format: uuid
        label:
          type: string
        votes:
          type: integer
      required:
      - id
      - poll_id
      - label
      - votes
    PollRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        question:
          type: string
        is_active:
          type: boolean
        created_at:
          type: string
          format: date-time
      required:
      - id
      - question
      - is_active
      - created_at
    PositionCreateReq:
      type: object
      properties:
        name:
          type: string
        level:
          type: integer
      required:
      - name
    PositionRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        level:
          type: integer
        created_at:
          type: string
          format: date-time
      required:
      - id
      - name
      - level
      - created_at
    PositionUpdateReq:
      type: object
      properties:
        name:
          type: string
        level:
          type: integer
    PreviewQuery:
      type: object
      properties:
        period: *id086
      required:
      - period
    PreviewRow:
      type: object
      properties:
        employee_id:
          type: string
          format: uuid
        name:
          type: string
        net_points:
          type: number
        earn_points:
          type: number
        deduct_points:
          type: number
        rate_idr:
          type: number
        amount_idr:
          type: integer
      required:
      - employee_id
      - name
      - net_points
      - earn_points
      - deduct_points
      - rate_idr
      - amount_idr
    ProductionConsumptionRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        run_id:
          type: string
          format: uuid
        raw_item_id:
          type: string
          format: uuid
        lot_id:
          type: string
          format: uuid
        qty:
          type: integer
      required:
      - id
      - run_id
      - raw_item_id
      - qty
    ProductionRunRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        batch_id:
          type: string
          format: uuid
        work_center_id:
          type: string
          format: uuid
        operator_employee_id:
          type: string
          format: uuid
        started_at:
          type: string
          format: date-time
        paused_total_secs:
          type: integer
        ended_at:
          type: string
          format: date-time
        output_qty:
          type: integer
        scrap_qty:
          type: integer
        created_at:
          type: string
          format: date-time
      required:
      - id
      - batch_id
      - paused_total_secs
      - output_qty
      - scrap_qty
      - created_at
    ProjectRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        description:
          type: string
        status:
          type: string
        owner_employee_id:
          type: string
          format: uuid
        start_date:
          type: string
          format: date
        due_date:
          type: string
          format: date
        created_at:
          type: string
          format: date-time
      required:
      - id
      - name
      - status
      - created_at
    PushSubReq:
      type: object
      properties:
        endpoint:
          type: string
        p256dh:
          type: string
        auth:
          type: string
      required:
      - endpoint
      - p256dh
      - auth
    PushUnsubReq:
      type: object
      properties:
        endpoint:
          type: string
      required:
      - endpoint
    RangeQuery:
      type: object
      properties:
        from: *id006
        to: *id007
    RefreshReq:
      type: object
      properties:
        refresh_token:
          type: string
      required:
      - refresh_token
    RegisterFcmReq:
      type: object
      properties:
        token:
          type: string
        platform:
          type: string
    ReimbursementRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        employee_id:
          type: string
          format: uuid
        type_id:
          type: string
          format: uuid
        amount:
          type: integer
        description:
          type: string
        receipt_url:
          type: string
        approval_request_id:
          type: string
          format: uuid
        approval_status:
          type: string
        current_step:
          type: integer
        created_at:
          type: string
          format: date-time
      required:
      - id
      - employee_id
      - type_id
      - amount
      - description
      - created_at
    ReimbursementTypeRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        created_at:
          type: string
          format: date-time
      required:
      - id
      - name
      - created_at
    RepaymentRow:
      type: object
      properties:
        period:
          type: string
        amount:
          type: integer
        run_id:
          type: string
          format: uuid
      required:
      - period
      - amount
      - run_id
    ReportQuery:
      type: object
      properties:
        from: *id087
        to: *id088
    SalaryStructureRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        employee_id:
          type: string
          format: uuid
        base_salary:
          type: integer
        effective_date:
          type: string
          format: date
        created_at:
          type: string
          format: date-time
      required:
      - id
      - employee_id
      - base_salary
      - effective_date
      - created_at
    SearchQuery:
      type: object
      properties:
        q: *id089
        page: *id090
        per_page: *id091
    SetBalanceReq:
      type: object
      properties:
        employee_id:
          type: string
          format: uuid
        leave_type_id:
          type: string
          format: uuid
        year:
          type: integer
        entitled_days:
          type: integer
      required:
      - employee_id
      - leave_type_id
      - year
      - entitled_days
    SetSalaryReq:
      type: object
      properties:
        base_salary:
          type: integer
        effective_date:
          type: string
          format: date
      required:
      - base_salary
      - effective_date
    ShiftRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        start_time:
          type: string
        end_time:
          type: string
        grace_minutes:
          type: integer
      required:
      - id
      - name
      - start_time
      - end_time
      - grace_minutes
    SlipQuery:
      type: object
      properties:
        per_page: *id092
    SpaceRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        description:
          type: string
        created_at:
          type: string
          format: date-time
      required:
      - id
      - name
      - created_at
    SprintRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        project_id:
          type: string
          format: uuid
        name:
          type: string
        goal:
          type: string
        start_date:
          type: string
          format: date
        end_date:
          type: string
          format: date
        status:
          type: string
        created_at:
          type: string
          format: date-time
      required:
      - id
      - project_id
      - name
      - status
      - created_at
    StartRunReq:
      type: object
      properties:
        batch_id:
          type: string
          format: uuid
        work_center_id:
          type: string
          format: uuid
    StopRunReq:
      type: object
      properties:
        output_qty:
          type: integer
        scrap_qty:
          type: integer
        location_id:
          type: string
          format: uuid
        output_lot_code:
          type: string
        consumption:
          type: array
          items:
            $ref: '#/components/schemas/ConsumptionReq'
    SubmitLeaveReq:
      type: object
      properties:
        employee_id:
          type: string
          format: uuid
        leave_type_id:
          type: string
          format: uuid
        start_date:
          type: string
          format: date
        end_date:
          type: string
          format: date
        reason:
          type: string
      required:
      - leave_type_id
      - start_date
      - end_date
    SubmitOvertimeReq:
      type: object
      properties:
        employee_id:
          type: string
          format: uuid
        work_date:
          type: string
          format: date
        minutes:
          type: integer
        reason:
          type: string
      required:
      - work_date
      - minutes
    SubmitReq:
      type: object
      properties:
        employee_id:
          type: string
          format: uuid
        type_id:
          type: string
          format: uuid
        amount:
          type: integer
        description:
          type: string
        receipt_url:
          type: string
    SuggestTripsReq:
      type: object
      properties:
        max_per_trip:
          type: integer
        cluster_radius_km:
          type: number
        origin_lat:
          type: number
        origin_lng:
          type: number
        vehicle_id:
          type: string
          format: uuid
    SurveyQuestionRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        survey_id:
          type: string
          format: uuid
        text:
          type: string
        position:
          type: integer
      required:
      - id
      - survey_id
      - text
      - position
    SurveyResponseRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        survey_id:
          type: string
          format: uuid
        question_id:
          type: string
          format: uuid
        respondent_membership_id:
          type: string
          format: uuid
        answer:
          type: string
        created_at:
          type: string
          format: date-time
      required:
      - id
      - survey_id
      - question_id
      - answer
      - created_at
    SurveyRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        title:
          type: string
        is_active:
          type: boolean
        created_at:
          type: string
          format: date-time
      required:
      - id
      - title
      - is_active
      - created_at
    SwitchReq:
      type: object
      properties:
        tenant_id:
          type: string
          format: uuid
      required:
      - tenant_id
    TagQuery:
      type: object
      properties:
        tag: *id093
        page: *id094
        per_page: *id095
    TaskCommentRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        task_id:
          type: string
          format: uuid
        author_membership_id:
          type: string
          format: uuid
        body:
          type: string
        created_at:
          type: string
          format: date-time
      required:
      - id
      - task_id
      - body
      - created_at
    TaskRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        project_id:
          type: string
          format: uuid
        status_id:
          type: string
          format: uuid
        title:
          type: string
        description:
          type: string
        assignee_employee_id:
          type: string
          format: uuid
        priority:
          type: string
        due_date:
          type: string
          format: date
        position:
          type: integer
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
      - id
      - project_id
      - title
      - priority
      - position
      - created_at
      - updated_at
    TaskStatusRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        project_id:
          type: string
          format: uuid
        name:
          type: string
        position:
          type: integer
        created_at:
          type: string
          format: date-time
      required:
      - id
      - project_id
      - name
      - position
      - created_at
    TeamCreateReq:
      type: object
      properties:
        name:
          type: string
        department_id:
          type: string
          format: uuid
      required:
      - name
    TeamRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        department_id:
          type: string
          format: uuid
        created_at:
          type: string
          format: date-time
      required:
      - id
      - name
      - created_at
    TeamUpdateReq:
      type: object
      properties:
        name:
          type: string
        department_id:
          type: string
          format: uuid
    TestCaseRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        project_id:
          type: string
          format: uuid
        title:
          type: string
        steps:
          type: string
        expected:
          type: string
        priority:
          type: string
        status:
          type: string
        created_at:
          type: string
          format: date-time
      required:
      - id
      - title
      - priority
      - status
      - created_at
    ThreadMessageRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        thread_id:
          type: string
          format: uuid
        author_membership_id:
          type: string
          format: uuid
        body:
          type: string
        created_at:
          type: string
          format: date-time
      required:
      - id
      - thread_id
      - body
      - created_at
    ThreadRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        title:
          type: string
        created_by_membership_id:
          type: string
          format: uuid
        created_at:
          type: string
          format: date-time
      required:
      - id
      - title
      - created_at
    TimesheetRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        employee_id:
          type: string
          format: uuid
        work_date:
          type: string
          format: date
        minutes:
          type: integer
        task_id:
          type: string
          format: uuid
        project_id:
          type: string
          format: uuid
        description:
          type: string
      required:
      - id
      - employee_id
      - work_date
      - minutes
    TokenPair:
      type: object
      properties:
        access_token:
          type: string
        refresh_token:
          type: string
        token_type:
          type: string
      required:
      - access_token
      - refresh_token
      - token_type
    TripFromSuggestionReq:
      type: object
      properties:
        vehicle_id:
          type: string
          format: uuid
        driver_employee_id:
          type: string
          format: uuid
        helper_employee_id:
          type: string
          format: uuid
        origin_location_id:
          type: string
          format: uuid
        do_ids:
          type: array
          items:
            type: string
            format: uuid
      required:
      - vehicle_id
      - do_ids
    TripListRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        vehicle_id:
          type: string
          format: uuid
        driver_employee_id:
          type: string
          format: uuid
        helper_employee_id:
          type: string
          format: uuid
        supervisor_employee_id:
          type: string
          format: uuid
        origin_location_id:
          type: string
          format: uuid
        source:
          type: string
        status:
          type: string
        started_at:
          type: string
          format: date-time
        ended_at:
          type: string
          format: date-time
        created_at:
          type: string
          format: date-time
        stops_count:
          type: integer
        delivered_count:
          type: integer
      required:
      - id
      - source
      - status
      - created_at
      - stops_count
      - delivered_count
    TripRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        vehicle_id:
          type: string
          format: uuid
        driver_employee_id:
          type: string
          format: uuid
        helper_employee_id:
          type: string
          format: uuid
        supervisor_employee_id:
          type: string
          format: uuid
        origin_location_id:
          type: string
          format: uuid
        source:
          type: string
        status:
          type: string
        started_at:
          type: string
          format: date-time
        ended_at:
          type: string
          format: date-time
        created_at:
          type: string
          format: date-time
      required:
      - id
      - source
      - status
      - created_at
    TripStopRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        trip_id:
          type: string
          format: uuid
        seq:
          type: integer
        so_id:
          type: string
          format: uuid
        customer_id:
          type: string
          format: uuid
        address:
          type: string
        lat:
          type: number
        lng:
          type: number
        status:
          type: string
        arrived_at:
          type: string
          format: date-time
        proof_photo_url:
          type: string
        note:
          type: string
        distance_km:
          type: number
        created_at:
          type: string
          format: date-time
      required:
      - id
      - trip_id
      - seq
      - status
      - created_at
    TugasQuery:
      type: object
      properties:
        selesai: *id096
        per_page: *id097
    UatRunRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        test_case_id:
          type: string
          format: uuid
        result:
          type: string
        notes:
          type: string
        run_at:
          type: string
          format: date-time
      required:
      - id
      - test_case_id
      - result
      - run_at
    UnregisterFcmReq:
      type: object
      properties:
        token:
          type: string
    UpdateIncidentReq:
      type: object
      properties:
        status:
          type: string
        resolution:
          type: string
        severity:
          type: string
    UpdateLoanReq:
      type: object
      properties:
        status:
          type: string
    UpdateTaskReq:
      type: object
      properties:
        title:
          type: string
        description:
          type: string
        assignee_employee_id:
          type: string
          format: uuid
        priority:
          type: string
        due_date:
          type: string
          format: date
    UpdateTripReq:
      type: object
      properties:
        vehicle_id:
          type: string
          format: uuid
        driver_employee_id:
          type: string
          format: uuid
        helper_employee_id:
          type: string
          format: uuid
        supervisor_employee_id:
          type: string
          format: uuid
        origin_location_id:
          type: string
          format: uuid
        status:
          type: string
        started_at:
          type: string
          format: date-time
        ended_at:
          type: string
          format: date-time
    UpdateVehicleReq:
      type: object
      properties:
        name:
          type: string
        plate:
          type: string
        year:
          type: integer
        capacity_kg:
          type: number
        capacity_vol:
          type: number
        status:
          type: string
        is_active:
          type: boolean
    VehicleRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        plate:
          type: string
        year:
          type: integer
        capacity_kg:
          type: number
        capacity_vol:
          type: number
        status:
          type: string
        is_active:
          type: boolean
        created_at:
          type: string
          format: date-time
      required:
      - id
      - plate
      - status
      - is_active
      - created_at
    VersionRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        article_id:
          type: string
          format: uuid
        version:
          type: integer
        title:
          type: string
        body:
          type: string
        created_at:
          type: string
          format: date-time
      required:
      - id
      - article_id
      - version
      - title
      - body
      - created_at
    WeeklyQuery:
      type: object
      properties:
        employee_id: *id098
        week_start: *id099
      required:
      - employee_id
      - week_start
    Galat:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
            message:
              type: string
