openapi: 3.0.1
info:
  description: Accountflow's public API for external integrations.
  title: Accountflow Bridge API
  version: 1.0.0-rc1
servers:
- url: https://api.next.accountflow.com
  description: next
security:
- oauth2: []
tags:
- description: "Bank accounts, transactions, and bank↔GL reconciliation"
  name: Bank & Reconciliation
- description: VAT filing terms and their status
  name: VAT
- description: "Chart of accounts, GL lines, and trial balances"
  name: General Ledger
- description: "Signed event notifications — poke, then fetch"
  name: Webhooks
- description: The accounting clients (companies) within your reach
  name: Companies
- description: Document metadata; binaries via signed URLs
  name: Documents
- description: The organization's users
  name: Users
- description: Access policies and their user assignments
  name: Access Management
- description: "Asynchronous work: submit via a resource endpoint, poll here"
  name: Jobs
- description: Verify your integration's credentials and scope
  name: Identity
paths:
  /v1/companies:
    get:
      description: Cursor-paginated list of the companies you can read. Iterate with
        next_cursor; cursors are bound to your credentials and this endpoint's filters.
      operationId: listCompanies
      parameters:
      - description: Opaque cursor from a previous page's pagination.next_cursor.
        in: query
        name: cursor
        required: false
        schema:
          type: string
      - description: "Rows per page (1–200, default 50)."
        in: query
        name: page_size
        required: false
        schema:
          type: integer
          format: int32
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PageCompanyResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - companies:read
      summary: List companies
      tags:
      - Companies
  /v1/companies/{companyId}:
    delete:
      description: "Soft delete: the company disappears from the API (404) and can\
        \ be restored with POST /restore. Idempotent."
      operationId: deleteCompany
      parameters:
      - in: path
        name: companyId
        required: true
        schema:
          type: string
          format: uuid
      - in: header
        name: Idempotency-Key
        required: false
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DeleteCompanyRequest"
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletionResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - companies:write
      summary: Delete a company
      tags:
      - Companies
    get:
      operationId: getCompany
      parameters:
      - in: path
        name: companyId
        required: true
        schema:
          type: string
          format: uuid
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CompanyResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - companies:read
      summary: Get a company
      tags:
      - Companies
    patch:
      description: "Patch semantics: absent fields stay unchanged."
      operationId: updateCompany
      parameters:
      - in: path
        name: companyId
        required: true
        schema:
          type: string
          format: uuid
      - in: header
        name: Idempotency-Key
        required: false
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateCompanyRequest"
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CompanyResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - companies:write
      summary: Update a company
      tags:
      - Companies
  /v1/companies/{companyId}/accounts:
    get:
      description: "The company's accounts for one accounting year. Omit `year` for\
        \ the current accounting year. Accounts are identified by their account number,\
        \ which is stable across years."
      operationId: listAccounts
      parameters:
      - in: path
        name: companyId
        required: true
        schema:
          type: string
          format: uuid
      - description: Accounting year; omitted = the current accounting year.
        in: query
        name: year
        required: false
        schema:
          type: integer
          format: int32
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PageLedgerAccountResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - ledger:read
      summary: List the chart of accounts
      tags:
      - General Ledger
  /v1/companies/{companyId}/accounts/{accountNumber}:
    get:
      description: "One account of the chart, addressed by its account number. Omit\
        \ `year` for the current accounting year."
      operationId: getAccount
      parameters:
      - in: path
        name: companyId
        required: true
        schema:
          type: string
          format: uuid
      - description: The account number.
        example: 3000
        in: path
        name: accountNumber
        required: true
        schema:
          type: string
      - description: Accounting year; omitted = the current accounting year.
        in: query
        name: year
        required: false
        schema:
          type: integer
          format: int32
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LedgerAccountResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - ledger:read
      summary: Get one account
      tags:
      - General Ledger
  /v1/companies/{companyId}/actions/disable:
    post:
      description: "Pauses the client: it stays readable with status 'disabled'. Idempotent."
      operationId: disableCompany
      parameters:
      - in: path
        name: companyId
        required: true
        schema:
          type: string
          format: uuid
      - in: header
        name: Idempotency-Key
        required: false
        schema:
          type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CompanyResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - companies:write
      summary: Disable a company
      tags:
      - Companies
  /v1/companies/{companyId}/actions/enable:
    post:
      description: Idempotent.
      operationId: enableCompany
      parameters:
      - in: path
        name: companyId
        required: true
        schema:
          type: string
          format: uuid
      - in: header
        name: Idempotency-Key
        required: false
        schema:
          type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CompanyResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - companies:write
      summary: Enable a company
      tags:
      - Companies
  /v1/companies/{companyId}/bank/accounts:
    get:
      operationId: listBankAccounts
      parameters:
      - in: path
        name: companyId
        required: true
        schema:
          type: string
          format: uuid
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PageBankAccount"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - bank:read
      summary: List the company's bank accounts
      tags:
      - Bank & Reconciliation
  /v1/companies/{companyId}/bank/accounts/{bankAccountId}/reconciliation-summary:
    get:
      description: "Twelve periods with group/entry counts, staleness, and the firm's\
        \ period-approval status on the mapped GL account."
      operationId: getReconciliationSummary
      parameters:
      - in: path
        name: companyId
        required: true
        schema:
          type: string
          format: uuid
      - in: path
        name: bankAccountId
        required: true
        schema:
          type: string
          format: uuid
      - in: query
        name: year
        required: true
        schema:
          type: integer
          format: int32
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ReconciliationSummary"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - reconciliation:read
      summary: Per-period reconciliation summary
      tags:
      - Bank & Reconciliation
  /v1/companies/{companyId}/bank/accounts/{bankAccountId}/reconciliations:
    get:
      operationId: listReconciliations
      parameters:
      - in: path
        name: companyId
        required: true
        schema:
          type: string
          format: uuid
      - in: path
        name: bankAccountId
        required: true
        schema:
          type: string
          format: uuid
      - in: query
        name: year
        required: false
        schema:
          type: integer
          format: int32
      - in: query
        name: period
        required: false
        schema:
          type: integer
          format: int32
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PageReconciliation"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - reconciliation:read
      summary: List reconciliation groups for a bank account
      tags:
      - Bank & Reconciliation
  /v1/companies/{companyId}/bank/accounts/{bankAccountId}/transactions:
    get:
      description: "Cursor-paginated, newest first. reconciliationStatus is computed\
        \ on every read: unreconciled | reconciled | stale."
      operationId: listBankTransactions
      parameters:
      - in: path
        name: companyId
        required: true
        schema:
          type: string
          format: uuid
      - in: path
        name: bankAccountId
        required: true
        schema:
          type: string
          format: uuid
      - in: query
        name: from
        required: false
        schema:
          type: string
          format: date
      - in: query
        name: to
        required: false
        schema:
          type: string
          format: date
      - description: unreconciled | reconciled | stale.
        in: query
        name: reconciliation_status
        required: false
        schema:
          type: string
      - in: query
        name: cursor
        required: false
        schema:
          type: string
      - in: query
        name: page_size
        required: false
        schema:
          type: integer
          format: int32
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PageBankTransaction"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - bank:read
      summary: List bank transactions
      tags:
      - Bank & Reconciliation
  /v1/companies/{companyId}/bank/reconciliations:
    post:
      description: Creates one reconciliation group for a period. A GL line already
        reconciled that year is a 409. Correction = delete and recreate — groups are
        not editable.
      operationId: createReconciliation
      parameters:
      - in: path
        name: companyId
        required: true
        schema:
          type: string
          format: uuid
      - in: header
        name: Idempotency-Key
        required: false
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateReconciliationRequest"
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Reconciliation"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - reconciliation:write
      summary: Reconcile bank transactions against GL lines
      tags:
      - Bank & Reconciliation
  /v1/companies/{companyId}/documents:
    get:
      operationId: listDocuments
      parameters:
      - in: path
        name: companyId
        required: true
        schema:
          type: string
          format: uuid
      - description: Opaque cursor from a previous page.
        in: query
        name: cursor
        required: false
        schema:
          type: string
      - description: "Rows per page (1–200, default 50)."
        in: query
        name: page_size
        required: false
        schema:
          type: integer
          format: int32
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PageDocumentResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - documents:read
      summary: List a company's documents
      tags:
      - Documents
    post:
      description: "Step 1 of 2: declare the document (JSON), receive a signed PUT\
        \ URL, upload the binary directly to storage, then POST /complete. The document\
        \ does not exist until completion."
      operationId: createDocumentUpload
      parameters:
      - in: path
        name: companyId
        required: true
        schema:
          type: string
          format: uuid
      - in: header
        name: Idempotency-Key
        required: false
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateUploadRequest"
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DocumentUploadResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - documents:write
      summary: Start an upload
      tags:
      - Documents
  /v1/companies/{companyId}/general-ledger/lines:
    get:
      description: "Cursor-paginated GL lines for one accounting year, ordered by\
        \ period then line id. Zero-amount lines are included. Line ids are stable\
        \ across re-imports."
      operationId: listGeneralLedgerLines
      parameters:
      - in: path
        name: companyId
        required: true
        schema:
          type: string
          format: uuid
      - description: Accounting year; omitted = the current accounting year.
        in: query
        name: year
        required: false
        schema:
          type: integer
          format: int32
      - description: Filter to one account number.
        example: 3000
        in: query
        name: account
        required: false
        schema:
          type: string
      - description: Filter to one fiscal period (1–12).
        in: query
        name: period
        required: false
        schema:
          type: integer
          format: int32
      - description: Opaque cursor from a previous page.
        in: query
        name: cursor
        required: false
        schema:
          type: string
      - description: "Rows per page (1–200, default 50)."
        in: query
        name: page_size
        required: false
        schema:
          type: integer
          format: int32
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PageGlLineResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - ledger:read
      summary: List general-ledger lines
      tags:
      - General Ledger
  /v1/companies/{companyId}/reconciliations/{reconciliationId}:
    delete:
      description: Dissolves the group; its transactions and GL lines become unreconciled
        again.
      operationId: deleteReconciliation
      parameters:
      - in: path
        name: companyId
        required: true
        schema:
          type: string
          format: uuid
      - in: path
        name: reconciliationId
        required: true
        schema:
          type: string
          format: uuid
      - in: header
        name: Idempotency-Key
        required: false
        schema:
          type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ReconciliationDeletionResult"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - reconciliation:write
      summary: Unmatch a reconciliation group
      tags:
      - Bank & Reconciliation
    get:
      operationId: getReconciliation
      parameters:
      - in: path
        name: companyId
        required: true
        schema:
          type: string
          format: uuid
      - in: path
        name: reconciliationId
        required: true
        schema:
          type: string
          format: uuid
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Reconciliation"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - reconciliation:read
      summary: Get one reconciliation group with its entries
      tags:
      - Bank & Reconciliation
  /v1/companies/{companyId}/restore:
    post:
      description: Brings a soft-deleted company back in its prior state (active or
        disabled). Refused (409) when the deletion was part of an organization cascade.
      operationId: restoreCompany
      parameters:
      - in: path
        name: companyId
        required: true
        schema:
          type: string
          format: uuid
      - in: header
        name: Idempotency-Key
        required: false
        schema:
          type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CompanyResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - companies:write
      summary: Restore a deleted company
      tags:
      - Companies
  /v1/companies/{companyId}/trial-balance:
    get:
      description: "Opening balance, labeled monthly movements, and closing balance\
        \ per account for one accounting year. The closing balance at period N is\
        \ openingBalance plus the movements of periods 1..N."
      operationId: getTrialBalance
      parameters:
      - in: path
        name: companyId
        required: true
        schema:
          type: string
          format: uuid
      - description: Accounting year; omitted = the current accounting year.
        in: query
        name: year
        required: false
        schema:
          type: integer
          format: int32
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TrialBalanceResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - ledger:read
      summary: Get the trial balance
      tags:
      - General Ledger
  /v1/companies/{companyId}/vat/terms:
    get:
      description: The company's VAT filing terms with their reconciliation status
        and totals (decimal NOK).
      operationId: listVatTerms
      parameters:
      - in: path
        name: companyId
        required: true
        schema:
          type: string
          format: uuid
      - description: Filter to one filing year.
        in: query
        name: year
        required: false
        schema:
          type: integer
          format: int32
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PageVatTermResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - vat:read
      summary: List VAT filing terms
      tags:
      - VAT
  /v1/documents/{documentId}:
    delete:
      description: "Soft delete: the document 404s until restored; the binary stays\
        \ in storage. Idempotent."
      operationId: deleteDocument
      parameters:
      - in: path
        name: documentId
        required: true
        schema:
          type: string
          format: uuid
      - in: header
        name: Idempotency-Key
        required: false
        schema:
          type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DocumentDeletionResult"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - documents:write
      summary: Delete a document
      tags:
      - Documents
    get:
      operationId: getDocument
      parameters:
      - in: path
        name: documentId
        required: true
        schema:
          type: string
          format: uuid
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DocumentResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - documents:read
      summary: Get a document's metadata
      tags:
      - Documents
    patch:
      operationId: renameDocument
      parameters:
      - in: path
        name: documentId
        required: true
        schema:
          type: string
          format: uuid
      - in: header
        name: Idempotency-Key
        required: false
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RenameRequest"
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DocumentResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - documents:write
      summary: Rename a document
      tags:
      - Documents
  /v1/documents/{documentId}/complete:
    post:
      description: "Step 2 of 2: after the binary is uploaded, verifies the object\
        \ (size and content type must match the declaration) and creates the document.\
        \ Emits document.uploaded."
      operationId: completeDocumentUpload
      parameters:
      - in: path
        name: documentId
        required: true
        schema:
          type: string
          format: uuid
      - in: header
        name: Idempotency-Key
        required: false
        schema:
          type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DocumentResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - documents:write
      summary: Complete an upload
      tags:
      - Documents
  /v1/documents/{documentId}/download-url:
    get:
      description: Returns a short-lived signed GET URL for the binary. Every issue
        is recorded in the document's access log.
      operationId: getDocumentDownloadUrl
      parameters:
      - in: path
        name: documentId
        required: true
        schema:
          type: string
          format: uuid
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DownloadUrlResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - documents:read
      summary: Get a download URL
      tags:
      - Documents
  /v1/documents/{documentId}/restore:
    post:
      description: Idempotent.
      operationId: restoreDocument
      parameters:
      - in: path
        name: documentId
        required: true
        schema:
          type: string
          format: uuid
      - in: header
        name: Idempotency-Key
        required: false
        schema:
          type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DocumentResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - documents:write
      summary: Restore a deleted document
      tags:
      - Documents
  /v1/jobs/{jobId}:
    get:
      description: "Visible only to the API client that submitted it. Poll until status\
        \ is succeeded, failed, or expired; job completion also produces a job.succeeded/job.failed\
        \ webhook event."
      operationId: getJob
      parameters:
      - in: path
        name: jobId
        required: true
        schema:
          type: string
          format: uuid
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/JobResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2: []
      summary: Get a job's status
      tags:
      - Jobs
  /v1/organizations/{organizationId}/access-policies:
    get:
      operationId: listAccessPolicies
      parameters:
      - in: path
        name: organizationId
        required: true
        schema:
          type: string
          format: uuid
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PageAccessPolicyResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - access:manage
      summary: List access policies
      tags:
      - Access Management
    post:
      description: "The policy is one document: name, default flag, and its rules.\
        \ Marking it default demotes the previous default."
      operationId: createAccessPolicy
      parameters:
      - in: path
        name: organizationId
        required: true
        schema:
          type: string
          format: uuid
      - in: header
        name: Idempotency-Key
        required: false
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreatePolicyRequest"
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccessPolicyResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - access:manage
      summary: Create an access policy
      tags:
      - Access Management
  /v1/organizations/{organizationId}/access-policies/{policyId}:
    get:
      operationId: getAccessPolicy
      parameters:
      - in: path
        name: organizationId
        required: true
        schema:
          type: string
          format: uuid
      - in: path
        name: policyId
        required: true
        schema:
          type: string
          format: uuid
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccessPolicyResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - access:manage
      summary: Get one access policy
      tags:
      - Access Management
    patch:
      description: "Patch semantics for the scalar fields; a rules array, when present,\
        \ replaces the whole rule set."
      operationId: updateAccessPolicy
      parameters:
      - in: path
        name: organizationId
        required: true
        schema:
          type: string
          format: uuid
      - in: path
        name: policyId
        required: true
        schema:
          type: string
          format: uuid
      - in: header
        name: Idempotency-Key
        required: false
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdatePolicyRequest"
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccessPolicyResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - access:manage
      summary: Update an access policy
      tags:
      - Access Management
  /v1/organizations/{organizationId}/access-policies/{policyId}/actions/archive:
    post:
      description: Archived policies grant nothing and stay for audit; they are never
        hard-deleted. Idempotent.
      operationId: archiveAccessPolicy
      parameters:
      - in: path
        name: organizationId
        required: true
        schema:
          type: string
          format: uuid
      - in: path
        name: policyId
        required: true
        schema:
          type: string
          format: uuid
      - in: header
        name: Idempotency-Key
        required: false
        schema:
          type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccessPolicyResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - access:manage
      summary: Archive an access policy
      tags:
      - Access Management
  /v1/organizations/{organizationId}/access-policies/{policyId}/assignments:
    post:
      description: The user's effective access becomes the union of all assigned policies
        plus the organization's default policy.
      operationId: assignAccess
      parameters:
      - in: path
        name: organizationId
        required: true
        schema:
          type: string
          format: uuid
      - in: path
        name: policyId
        required: true
        schema:
          type: string
          format: uuid
      - in: header
        name: Idempotency-Key
        required: false
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AssignRequest"
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AssignmentResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - access:manage
      summary: Assign a user to a policy
      tags:
      - Access Management
  /v1/organizations/{organizationId}/access-policies/{policyId}/assignments/{userId}:
    delete:
      operationId: revokeAccess
      parameters:
      - in: path
        name: organizationId
        required: true
        schema:
          type: string
          format: uuid
      - in: path
        name: policyId
        required: true
        schema:
          type: string
          format: uuid
      - in: path
        name: userId
        required: true
        schema:
          type: string
          format: uuid
      - in: header
        name: Idempotency-Key
        required: false
        schema:
          type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccessRevocationResult"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - access:manage
      summary: Revoke a user's assignment
      tags:
      - Access Management
  /v1/organizations/{organizationId}/companies:
    post:
      description: "Registers a new accounting client under one of your organizations,\
        \ provisions its settings and first ledger, and grants access per the organization's\
        \ policies."
      operationId: createCompany
      parameters:
      - in: path
        name: organizationId
        required: true
        schema:
          type: string
          format: uuid
      - in: header
        name: Idempotency-Key
        required: false
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateCompanyRequest"
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CompanyResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - companies:write
      summary: Create a company
      tags:
      - Companies
  /v1/organizations/{organizationId}/users:
    get:
      description: Members of the organization. Disabled users are listed with status
        'disabled'; deleted users are not represented.
      operationId: listOrgUsers
      parameters:
      - in: path
        name: organizationId
        required: true
        schema:
          type: string
          format: uuid
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PageUserResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - users:read
      summary: List the organization's users
      tags:
      - Users
  /v1/organizations/{organizationId}/users/{userId}:
    get:
      operationId: getUser
      parameters:
      - in: path
        name: organizationId
        required: true
        schema:
          type: string
          format: uuid
      - in: path
        name: userId
        required: true
        schema:
          type: string
          format: uuid
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UserResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - users:read
      summary: Get one user
      tags:
      - Users
  /v1/organizations/{organizationId}/webhooks:
    get:
      operationId: listWebhookEndpoints
      parameters:
      - in: path
        name: organizationId
        required: true
        schema:
          type: string
          format: uuid
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PageWebhookEndpointResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - webhooks:manage
      summary: List webhook endpoints
      tags:
      - Webhooks
    post:
      description: The response carries the signing secret EXACTLY ONCE. The endpoint
        starts in pending_verification and must echo the verification challenge (a
        webhook.verification POST) with a 2xx before events flow.
      operationId: createWebhookEndpoint
      parameters:
      - in: path
        name: organizationId
        required: true
        schema:
          type: string
          format: uuid
      - in: header
        name: Idempotency-Key
        required: false
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateWebhookRequest"
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookEndpointCreatedResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - webhooks:manage
      summary: Register a webhook endpoint
      tags:
      - Webhooks
  /v1/organizations/{organizationId}/webhooks/{webhookId}:
    delete:
      operationId: deleteWebhookEndpoint
      parameters:
      - in: path
        name: organizationId
        required: true
        schema:
          type: string
          format: uuid
      - in: path
        name: webhookId
        required: true
        schema:
          type: string
          format: uuid
      - in: header
        name: Idempotency-Key
        required: false
        schema:
          type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookEndpointDeletionResult"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - webhooks:manage
      summary: Delete a webhook endpoint
      tags:
      - Webhooks
    get:
      operationId: getWebhookEndpoint
      parameters:
      - in: path
        name: organizationId
        required: true
        schema:
          type: string
          format: uuid
      - in: path
        name: webhookId
        required: true
        schema:
          type: string
          format: uuid
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookEndpointResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - webhooks:manage
      summary: Get one webhook endpoint
      tags:
      - Webhooks
    patch:
      description: Patch semantics. Changing the URL puts the endpoint back into pending_verification
        until the new URL answers the challenge.
      operationId: updateWebhookEndpoint
      parameters:
      - in: path
        name: organizationId
        required: true
        schema:
          type: string
          format: uuid
      - in: path
        name: webhookId
        required: true
        schema:
          type: string
          format: uuid
      - in: header
        name: Idempotency-Key
        required: false
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateWebhookRequest"
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookEndpointResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - webhooks:manage
      summary: Update a webhook endpoint
      tags:
      - Webhooks
  /v1/organizations/{organizationId}/webhooks/{webhookId}/actions/rotate-secret:
    post:
      description: Returns the NEW secret exactly once; old signatures stop validating
        immediately.
      operationId: rotateWebhookSecret
      parameters:
      - in: path
        name: organizationId
        required: true
        schema:
          type: string
          format: uuid
      - in: path
        name: webhookId
        required: true
        schema:
          type: string
          format: uuid
      - in: header
        name: Idempotency-Key
        required: false
        schema:
          type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookEndpointCreatedResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - webhooks:manage
      summary: Rotate the signing secret
      tags:
      - Webhooks
  /v1/organizations/{organizationId}/webhooks/{webhookId}/deliveries:
    get:
      description: "The self-debugging surface: every attempt with status code, error,\
        \ and payload, newest first, cursor-paginated."
      operationId: listWebhookDeliveries
      parameters:
      - in: path
        name: organizationId
        required: true
        schema:
          type: string
          format: uuid
      - in: path
        name: webhookId
        required: true
        schema:
          type: string
          format: uuid
      - description: Opaque cursor from a previous page.
        in: query
        name: cursor
        required: false
        schema:
          type: string
      - description: "Rows per page (1–200, default 50)."
        in: query
        name: page_size
        required: false
        schema:
          type: integer
          format: int32
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PageWebhookDeliveryResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      security:
      - oauth2:
        - webhooks:manage
      summary: List delivery attempts
      tags:
      - Webhooks
  /v1/whoami:
    get:
      description: "Returns the authenticated API client's identity, mode, and organization\
        \ scope. Use it to verify credentials before calling anything else."
      operationId: getCallerIdentity
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhoamiResponse"
          description: OK
        "400":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: displayName must not be blank
                    error: validation_failed
                    requestId: d4f0a1b2-…
                    status: 400
                    title: Validation Failed
                    type: https://api.accountflow.com/errors/validation-failed
              schema:
                $ref: "#/components/schemas/Problem"
          description: Malformed request — a required field is missing or a value
            is out of range. The `detail` names the field.
        "500":
          content:
            application/problem+json:
              examples:
                problem:
                  value:
                    detail: Unexpected error
                    error: internal_error
                    requestId: d4f0a1b2-…
                    status: 500
                    title: Internal Server Error
                    type: https://api.accountflow.com/errors/internal-error
              schema:
                $ref: "#/components/schemas/Problem"
          description: "Unexpected server error. Nothing about the request was wrong\
            \ — retry is safe, and mutations retried with the same Idempotency-Key\
            \ are deduplicated."
      summary: Who am I
      tags:
      - Identity
components:
  schemas:
    AccessPolicyResponse:
      type: object
      description: "An access policy: a named set of company access rules, assignable\
        \ to users."
      properties:
        createdAt:
          type: string
          format: date-time
        description:
          type: string
          nullable: true
        id:
          type: string
          format: uuid
          description: The policy's stable identifier.
        isDefault:
          type: boolean
          description: "The org's baseline policy, applied to every member on top\
            \ of explicit grants."
        name:
          type: string
          example: Standard bookkeeper
        rules:
          type: array
          items:
            $ref: "#/components/schemas/Rule"
        status:
          type: string
          description: '''ACTIVE'' or ''ARCHIVED''. Archived policies grant nothing
            and are kept for audit.'
    AccessRevocationResult:
      type: object
      description: Confirmation of a revocation.
      properties:
        policyId:
          type: string
          format: uuid
        status:
          type: string
          example: revoked
        userId:
          type: string
          format: uuid
    AssignRequest:
      type: object
      properties:
        userId:
          type: string
          format: uuid
    AssignmentResponse:
      type: object
      description: A user's assignment to an access policy.
      properties:
        id:
          type: string
          format: uuid
          description: The assignment's stable identifier.
        policyId:
          type: string
          format: uuid
        userId:
          type: string
          format: uuid
    Association:
      type: object
      description: "An attachment target: the whole company, or one voucher (transaction)."
      nullable: true
      properties:
        type:
          type: string
          example: transaction
        value:
          type: string
          description: "The company id, or the voucher's transaction key."
    AssociationInput:
      type: object
      properties:
        type:
          type: string
          example: transaction
        value:
          type: string
          description: Required for 'transaction'; ignored for 'company'.
          nullable: true
    BankAccount:
      type: object
      description: A bank account connected to the company.
      properties:
        accountNumber:
          type: string
          nullable: true
        accountType:
          type: string
          nullable: true
        availableBalance:
          type: number
          nullable: true
        balanceUpdatedAt:
          type: string
          format: date-time
          description: When the balances were last synced from the bank.
          nullable: true
        bic:
          type: string
          nullable: true
        bookedBalance:
          type: number
          nullable: true
        currencyCode:
          type: string
          example: NOK
          nullable: true
        glAccountNumber:
          type: string
          description: "The mapped GL account number, when reconciliation is set up."
          nullable: true
        iban:
          type: string
          nullable: true
        id:
          type: string
          format: uuid
          description: The account's stable identifier.
        name:
          type: string
          nullable: true
        status:
          type: string
          description: active | closed | inactive.
          example: active
    BankEntry:
      type: object
      description: One matched bank transaction.
      properties:
        amount:
          type: number
        transactionId:
          type: string
          format: uuid
    BankTransaction:
      type: object
      description: A bank transaction with its computed reconciliation state.
      properties:
        amount:
          type: number
          description: Signed amount; debits are negative.
        bookingDate:
          type: string
          format: date
          nullable: true
        counterpartyAccount:
          type: string
          nullable: true
        counterpartyName:
          type: string
          nullable: true
        currencyCode:
          type: string
          example: NOK
          nullable: true
        description:
          type: string
          nullable: true
        id:
          type: string
          format: uuid
          description: The transaction's stable identifier.
        reconciliationId:
          type: string
          format: uuid
          description: "The reconciliation group, when matched."
          nullable: true
        reconciliationStatus:
          type: string
          description: "Computed on every read: unreconciled | reconciled | stale.\
            \ Stale means the matched data changed after matching — re-reconcile."
          example: reconciled
        remittanceInfo:
          type: string
          nullable: true
        source:
          type: string
          description: provider_api | file_upload.
        status:
          type: string
          description: booked | pending.
          example: booked
        valueDate:
          type: string
          format: date
          nullable: true
    CompanyResponse:
      type: object
      description: An accounting client (company) within your reach.
      properties:
        companyType:
          type: string
          description: Legal form.
          example: AS
          nullable: true
        countryCode:
          type: string
          description: ISO country code.
          example: "NO"
          nullable: true
        createdAt:
          type: string
          format: date-time
          description: When the company was registered in Accountflow.
        currentAccountingYear:
          type: integer
          format: int32
          description: The accounting year currently being worked.
          nullable: true
        fiscalYearEnd:
          type: string
          format: date
          description: End of the current fiscal year.
        fiscalYearStart:
          type: string
          format: date
          description: Start of the current fiscal year.
        id:
          type: string
          format: uuid
          description: The company's stable identifier.
        name:
          type: string
          description: Display name.
          example: Fjellheim Regnskap AS
        organizationId:
          type: string
          format: uuid
          description: The organization (accounting firm) this company belongs to.
        organizationNumber:
          type: string
          description: Norwegian organization number.
          example: "987654325"
          nullable: true
        periodType:
          type: string
          description: Accounting period granularity.
          example: MONTHLY
          nullable: true
        status:
          type: string
          description: "Lifecycle state: 'active' or 'disabled'. Deleted companies\
            \ are not represented — they 404 until restored."
          example: active
    CreateCompanyRequest:
      type: object
      properties:
        assetsEnabled:
          type: boolean
          nullable: true
        companyNumber:
          type: string
        companyType:
          type: string
          nullable: true
        countryCode:
          type: string
          nullable: true
        fiscalYearEnd:
          type: string
          format: date
          nullable: true
        fiscalYearStart:
          type: string
          format: date
          nullable: true
        mainAccountantUserId:
          type: string
          format: uuid
          nullable: true
        name:
          type: string
        obMonth:
          type: integer
          format: int32
          nullable: true
        ownerUserId:
          type: string
          format: uuid
        payrollEnabled:
          type: boolean
          nullable: true
        periodType:
          type: string
          nullable: true
        vatEnabled:
          type: boolean
          nullable: true
    CreatePolicyRequest:
      type: object
      properties:
        description:
          type: string
          nullable: true
        isDefault:
          type: boolean
          nullable: true
        name:
          type: string
        rules:
          type: array
          items:
            $ref: "#/components/schemas/RuleInput"
          nullable: true
    CreateReconciliationRequest:
      type: object
      properties:
        bankAccountId:
          type: string
          format: uuid
        lineIds:
          type: array
          items:
            type: string
          nullable: true
        note:
          type: string
          nullable: true
        period:
          type: integer
          format: int32
        periodYear:
          type: integer
          format: int32
        transactionIds:
          type: array
          items:
            type: string
            format: uuid
          nullable: true
    CreateUploadRequest:
      type: object
      properties:
        association:
          $ref: "#/components/schemas/AssociationInput"
        displayName:
          type: string
        mimeType:
          type: string
        sizeBytes:
          type: integer
          format: int64
    CreateWebhookRequest:
      type: object
      properties:
        description:
          type: string
          nullable: true
        eventTypes:
          type: array
          items:
            type: string
          nullable: true
        url:
          type: string
    DeleteCompanyRequest:
      type: object
      properties:
        reason:
          type: string
          nullable: true
    DeletionResponse:
      type: object
      description: Confirmation of a (soft) deletion; restore is available via POST
        /restore.
      properties:
        deletedAt:
          type: string
          format: date-time
        id:
          type: string
          format: uuid
        status:
          type: string
          example: deleted
    DocumentDeletionResult:
      type: object
      description: Confirmation of a (soft) deletion; restore via POST /restore.
      properties:
        id:
          type: string
          format: uuid
        status:
          type: string
          example: deleted
    DocumentResponse:
      type: object
      description: "A document. Download the binary via GET /v1/documents/{id}/download-url."
      properties:
        association:
          $ref: "#/components/schemas/Association"
        companyId:
          type: string
          format: uuid
        createdAt:
          type: string
          format: date-time
        createdBy:
          type: string
          format: uuid
          description: The identity that uploaded it (a user or a service account).
        displayName:
          type: string
          example: Faktura 2026-042.pdf
        id:
          type: string
          format: uuid
          description: The document's stable identifier.
        mimeType:
          type: string
          example: application/pdf
          nullable: true
        organizationId:
          type: string
          format: uuid
        sizeBytes:
          type: integer
          format: int64
          nullable: true
        updatedAt:
          type: string
          format: date-time
    DocumentUploadResponse:
      type: object
      description: "An upload slot: PUT the binary to the signed URL, then POST /complete."
      properties:
        displayName:
          type: string
        documentId:
          type: string
          format: uuid
          description: The id the document will have once completed.
        mimeType:
          type: string
        sizeBytes:
          type: integer
          format: int64
        upload:
          $ref: "#/components/schemas/Upload"
    DownloadUrlResponse:
      type: object
      description: A short-lived signed URL for the binary. Fetch promptly; request
        a fresh one when it expires.
      properties:
        expiresAt:
          type: string
          format: date-time
        filename:
          type: string
          example: Faktura 2026-042.pdf
        url:
          type: string
    GlEntry:
      type: object
      description: One matched GL line (by its stable line id).
      properties:
        amount:
          type: number
        lineId:
          type: string
          description: The GL line's stable identity.
    GlLineResponse:
      type: object
      description: A general-ledger line.
      properties:
        accountNumber:
          type: string
          description: The account number this line is posted to.
          example: "3000"
        amount:
          type: number
          description: Amount in the company's base currency (signed; debit positive).
        baseCurrency:
          type: string
          description: The base/reporting currency.
          example: NOK
          nullable: true
        description:
          type: string
          description: Line description.
          nullable: true
        exchangeRate:
          type: number
          description: base = transactionAmount × exchangeRate.
          nullable: true
        lineId:
          type: string
          description: Stable line identifier (survives re-imports).
        period:
          type: integer
          format: int32
          description: Fiscal period 1–12.
        postedDate:
          type: string
          format: date
          description: Posting date (bokføringsdato).
          nullable: true
        taxAmount:
          type: number
          description: "VAT amount on the line, base currency."
          nullable: true
        taxCode:
          type: string
          description: The ERP's VAT code for the line.
          nullable: true
        transactionAmount:
          type: number
          description: Amount in the original document currency.
          nullable: true
        transactionCurrency:
          type: string
          description: The document currency.
          example: EUR
          nullable: true
        transactionDate:
          type: string
          format: date
          description: Document/transaction date (bilagsdato).
          nullable: true
        voucherNo:
          type: string
          description: The voucher (bilag) number this line belongs to.
          nullable: true
        year:
          type: integer
          format: int32
          description: Accounting year.
    JobResponse:
      type: object
      description: "An asynchronous job. Poll GET /v1/jobs/{jobId} until status is\
        \ terminal."
      properties:
        createdAt:
          type: string
          format: date-time
        error:
          type: string
          description: "Failure summary, present when status is failed."
          nullable: true
        finishedAt:
          type: string
          format: date-time
          nullable: true
        id:
          type: string
          format: uuid
          description: The job's stable identifier.
        params:
          type: object
        result:
          type: object
        startedAt:
          type: string
          format: date-time
          nullable: true
        status:
          type: string
          description: pending | running | succeeded | failed | expired.
          example: pending
        type:
          type: string
          description: The job type.
          example: document_export
    LedgerAccountResponse:
      type: object
      description: An account in the company's chart of accounts for one accounting
        year.
      properties:
        accountNumber:
          type: string
          description: The account number as it exists in the ERP.
          example: "3000"
        category:
          type: string
          description: ERP-supplied category.
          nullable: true
        currencyCode:
          type: string
          description: Account currency.
          example: NOK
          nullable: true
        hasActivity:
          type: boolean
          description: Whether the account has any GL activity this year.
        name:
          type: string
          description: Account name.
          example: "Salgsinntekt, avgiftspliktig"
          nullable: true
        saftCode:
          type: string
          description: "The mapped Norwegian SAF-T standard account code, when mapped."
          example: "3000"
          nullable: true
        year:
          type: integer
          format: int32
          description: The accounting year this chart belongs to.
          example: 2026
    PageAccessPolicyResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/AccessPolicyResponse"
        pagination:
          $ref: "#/components/schemas/Pagination"
    PageBankAccount:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/BankAccount"
        pagination:
          $ref: "#/components/schemas/Pagination"
    PageBankTransaction:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/BankTransaction"
        pagination:
          $ref: "#/components/schemas/Pagination"
    PageCompanyResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/CompanyResponse"
        pagination:
          $ref: "#/components/schemas/Pagination"
    PageDocumentResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/DocumentResponse"
        pagination:
          $ref: "#/components/schemas/Pagination"
    PageGlLineResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/GlLineResponse"
        pagination:
          $ref: "#/components/schemas/Pagination"
    PageLedgerAccountResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/LedgerAccountResponse"
        pagination:
          $ref: "#/components/schemas/Pagination"
    PageReconciliation:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Reconciliation"
        pagination:
          $ref: "#/components/schemas/Pagination"
    PageUserResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/UserResponse"
        pagination:
          $ref: "#/components/schemas/Pagination"
    PageVatTermResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/VatTermResponse"
        pagination:
          $ref: "#/components/schemas/Pagination"
    PageWebhookDeliveryResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/WebhookDeliveryResponse"
        pagination:
          $ref: "#/components/schemas/Pagination"
    PageWebhookEndpointResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/WebhookEndpointResponse"
        pagination:
          $ref: "#/components/schemas/Pagination"
    Pagination:
      type: object
      properties:
        has_more:
          type: boolean
        next_cursor:
          type: string
    PeriodMovement:
      type: object
      description: The movement of one fiscal period.
      properties:
        amount:
          type: number
        period:
          type: integer
          format: int32
          description: Fiscal period 1–12.
    PeriodSummary:
      type: object
      description: One period's state.
      properties:
        approvalStatus:
          type: string
          description: "The firm's period approval on the mapped GL account: APPROVED\
            \ | STALE | REVOKED; null = not approved (or no GL mapping)."
          nullable: true
        bankEntryCount:
          type: integer
          format: int32
        glEntryCount:
          type: integer
          format: int32
        groupCount:
          type: integer
          format: int32
        hasStale:
          type: boolean
          description: Any group in the period has gone stale.
        period:
          type: integer
          format: int32
    Problem:
      type: object
      description: RFC 9457 problem document — the shape of every error response.
        The `error` property carries the stable machine code from the error catalog.
      properties:
        detail:
          type: string
          description: Human-readable specifics
        error:
          type: string
          description: Stable machine code (see the error catalog)
        instance:
          type: string
        requestId:
          type: string
          description: Echo in support requests — it indexes the logs
        status:
          type: integer
        title:
          type: string
        type:
          type: string
          description: URI ending in the error's catalog slug
    Reconciliation:
      type: object
      description: "A reconciliation group: bank transactions matched against GL lines\
        \ for one period."
      properties:
        bankAccountId:
          type: string
          format: uuid
        bankAmountTotal:
          type: number
          description: Snapshot of the bank side at match time.
        bankEntries:
          type: array
          items:
            $ref: "#/components/schemas/BankEntry"
        createdAt:
          type: string
          format: date-time
        glAmountTotal:
          type: number
          description: Snapshot of the GL side at match time.
        glEntries:
          type: array
          items:
            $ref: "#/components/schemas/GlEntry"
        id:
          type: string
          format: uuid
        note:
          type: string
          nullable: true
        period:
          type: integer
          format: int32
          description: Fiscal period 1-12.
        periodYear:
          type: integer
          format: int32
        stale:
          type: boolean
          description: True when matched data changed after matching (either side).
    ReconciliationDeletionResult:
      type: object
      description: "Confirmation: the group is dissolved; its transactions and lines\
        \ are unreconciled again."
      properties:
        id:
          type: string
          format: uuid
        status:
          type: string
          example: deleted
    ReconciliationSummary:
      type: object
      description: The reconciliation picture for one bank account across a year.
      properties:
        bankAccountId:
          type: string
          format: uuid
        periods:
          type: array
          items:
            $ref: "#/components/schemas/PeriodSummary"
        year:
          type: integer
          format: int32
    RenameRequest:
      type: object
      properties:
        displayName:
          type: string
    Rule:
      type: object
      description: "One rule: a role on one company, or on every company (companyId\
        \ null)."
      properties:
        accountIds:
          type: array
          description: Account ids when accountScope is SPECIFIED; empty otherwise.
          items:
            type: string
            format: uuid
            description: Account ids when accountScope is SPECIFIED; empty otherwise.
        accountScope:
          type: string
          description: ALL or SPECIFIED.
          example: ALL
        companyId:
          type: string
          format: uuid
          description: The company; null = every company in the organization.
          nullable: true
        role:
          type: string
          description: "VIEWER, AUDITOR, ACCOUNTANT or ADMIN."
          example: ACCOUNTANT
    RuleInput:
      type: object
      description: One rule of a policy document.
      properties:
        accountIds:
          type: array
          items:
            type: string
            format: uuid
          nullable: true
        accountScope:
          type: string
          nullable: true
        companyId:
          type: string
          format: uuid
          description: The company; null = every company in the organization.
          nullable: true
        role:
          type: string
    TrialBalanceAccount:
      type: object
      description: "One account's opening balance, monthly movements, and closing\
        \ balance."
      properties:
        accountNumber:
          type: string
          example: "3000"
        category:
          type: string
          nullable: true
        closingBalance:
          type: number
          description: Balance at the end of the year.
        name:
          type: string
          nullable: true
        openingBalance:
          type: number
          description: Balance at the start of the year.
        periods:
          type: array
          description: Monthly movements (NOT running balances).
          items:
            $ref: "#/components/schemas/PeriodMovement"
    TrialBalanceResponse:
      type: object
      description: Trial balance for one accounting year.
      properties:
        accounts:
          type: array
          items:
            $ref: "#/components/schemas/TrialBalanceAccount"
        companyId:
          type: string
          format: uuid
          description: The company.
        ledgerId:
          type: string
          format: uuid
          description: The ledger this balance was read from (one per company × year).
        totals:
          $ref: "#/components/schemas/TrialBalanceTotals"
        year:
          type: integer
          format: int32
          description: Accounting year.
    TrialBalanceTotals:
      type: object
      description: Sums across all accounts.
      properties:
        closingBalance:
          type: number
        openingBalance:
          type: number
    UpdateCompanyRequest:
      type: object
      properties:
        companyNumber:
          type: string
          nullable: true
        companyType:
          type: string
          nullable: true
        countryCode:
          type: string
          nullable: true
        fiscalYearEnd:
          type: string
          format: date
          nullable: true
        fiscalYearStart:
          type: string
          format: date
          nullable: true
        name:
          type: string
          nullable: true
        periodType:
          type: string
          nullable: true
    UpdatePolicyRequest:
      type: object
      properties:
        description:
          type: string
          nullable: true
        isDefault:
          type: boolean
          nullable: true
        name:
          type: string
          nullable: true
        rules:
          type: array
          items:
            $ref: "#/components/schemas/RuleInput"
          nullable: true
    UpdateWebhookRequest:
      type: object
      properties:
        description:
          type: string
          nullable: true
        eventTypes:
          type: array
          items:
            type: string
          nullable: true
        url:
          type: string
          nullable: true
    Upload:
      type: object
      description: "Where and how to upload the binary — directly to storage, not\
        \ through the API."
      properties:
        contentType:
          type: string
          description: The Content-Type header the PUT must carry (it is part of the
            signature).
        expiresAt:
          type: string
          format: date-time
        method:
          type: string
          example: PUT
        url:
          type: string
          description: Signed PUT URL for the binary.
    UserResponse:
      type: object
      description: A user of the organization.
      properties:
        createdAt:
          type: string
          format: date-time
          description: When the user was registered.
          nullable: true
        email:
          type: string
          nullable: true
        firstName:
          type: string
          nullable: true
        id:
          type: string
          format: uuid
          description: The user's stable identifier.
        lastName:
          type: string
          nullable: true
        phone:
          type: string
          nullable: true
        role:
          type: string
          description: Membership role in this organization.
          example: MEMBER
        status:
          type: string
          description: "Lifecycle state: 'active' or 'disabled'."
          example: active
    VatTermResponse:
      type: object
      description: A VAT filing term and its current status.
      properties:
        calculatedTotal:
          type: number
          description: Calculated VAT total for the term (NOK).
          nullable: true
        difference:
          type: number
          description: calculated − filed (NOK).
          nullable: true
        filedTotal:
          type: number
          description: Filed VAT total per the tax authority (NOK).
          nullable: true
        id:
          type: string
          format: uuid
          description: The term's stable identifier.
        periodEnd:
          type: string
          format: date
          description: Last day of the term.
          nullable: true
        periodStart:
          type: string
          format: date
          description: First day of the term.
          nullable: true
        sequence:
          type: integer
          format: int32
          description: Term sequence within the year (1-based).
          example: 3
        status:
          type: string
          description: "Reconciliation status. Common values: approved_for_sending,\
            \ sent_to_egov, approved, reopened; earlier worker-side statuses may also\
            \ appear."
          nullable: true
        year:
          type: integer
          format: int32
          description: Filing year.
          example: 2026
    WebhookDeliveryResponse:
      type: object
      description: One delivery attempt log for an endpoint (self-debugging surface).
      properties:
        attempts:
          type: integer
          format: int32
        createdAt:
          type: string
          format: date-time
        deliveredAt:
          type: string
          format: date-time
          nullable: true
        eventId:
          type: string
          format: uuid
          description: The event this delivery carries — dedupe on this.
        eventType:
          type: string
        id:
          type: string
          format: uuid
          description: The delivery's identifier (retries share it).
        lastError:
          type: string
          nullable: true
        lastStatusCode:
          type: integer
          format: int32
          nullable: true
        payload:
          type: object
        status:
          type: string
          description: pending | delivered | dead.
          example: delivered
    WebhookEndpointCreatedResponse:
      type: object
      description: A newly registered (or secret-rotated) endpoint. The secret appears
        ONLY here — store it now.
      properties:
        endpoint:
          $ref: "#/components/schemas/WebhookEndpointResponse"
        secret:
          type: string
          description: The HMAC signing secret. Shown exactly once; rotate to get
            a new one.
    WebhookEndpointDeletionResult:
      type: object
      description: Confirmation of endpoint deletion.
      properties:
        id:
          type: string
          format: uuid
        status:
          type: string
          example: deleted
    WebhookEndpointResponse:
      type: object
      description: A webhook endpoint registration.
      properties:
        createdAt:
          type: string
          format: date-time
        description:
          type: string
          nullable: true
        disabledReason:
          type: string
          description: "Why the endpoint was disabled, when status is disabled."
          nullable: true
        eventTypes:
          type: array
          description: Subscribed event types; empty = every event type.
          items:
            type: string
            description: Subscribed event types; empty = every event type.
        id:
          type: string
          format: uuid
          description: The endpoint's stable identifier.
        status:
          type: string
          description: pending_verification | active | disabled. New endpoints must
            answer the verification challenge before deliveries start; repeated delivery
            failure auto-disables an endpoint.
          example: active
        url:
          type: string
    WhoamiResponse:
      type: object
      description: The authenticated principal's identity and scope.
      properties:
        clientId:
          type: string
          description: The integration's client id (the token's azp).
          example: com.acme.erp-sync
        mode:
          type: string
          description: "How this principal acts: SYSTEM, USER (headless, as its linked\
            \ human), or INTERACTIVE (a person, e.g. via an MCP client)."
          example: SYSTEM
        organizationId:
          type: string
          format: uuid
          description: The organization whose subtree this client reaches. Null for
            INTERACTIVE principals — a person's reach is their org memberships.
          nullable: true
  securitySchemes:
    oauth2:
      description: OAuth 2.0 against the Accountflow Keycloak realm. Server-to-server
        integrations use client credentials (scopes granted at provisioning); interactive
        clients (e.g. MCP) use authorization code with PKCE and request scopes explicitly.
      flows:
        authorizationCode:
          authorizationUrl: https://auth.next.accountflow.com/realms/accountflow/protocol/openid-connect/auth
          scopes:
            bank:read: Read bank data
            companies:read: Read companies
            companies:write: Manage companies
            documents:read: Read documents
            documents:write: Manage documents
            ledger:read: "Read GL, accounts, trial balances"
            reconciliation:read: Read reconciliation status
            reconciliation:write: Work with reconciliations
            users:read: Read organization users
            vat:read: Read VAT status and returns
          tokenUrl: https://auth.next.accountflow.com/realms/accountflow/protocol/openid-connect/token
        clientCredentials:
          scopes:
            access:manage: Manage access policies
            bank:read: Read bank data
            companies:read: Read companies
            companies:write: Manage companies
            documents:read: Read documents
            documents:write: Manage documents
            ledger:read: "Read GL, accounts, trial balances"
            reconciliation:read: Read reconciliation status
            reconciliation:write: Work with reconciliations
            users:read: Read organization users
            vat:read: Read VAT status and returns
            webhooks:manage: Manage webhook endpoints
          tokenUrl: https://auth.next.accountflow.com/realms/accountflow/protocol/openid-connect/token
      type: oauth2
