> ## Documentation Index
> Fetch the complete documentation index at: https://www.finta.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Create export

> Asynchronously export an income statement, balance sheet, or cash flow as PDF, CSV, or XLSX.
Requires API access: Manage and Reports: View. Does not edit company books; read-only OAuth
connections may request exports. Internal read-only keys cannot POST.
All report types require start_date and end_date. Balance sheet exports compare cumulative
balances across periods in that range; start_date selects displayed periods, not an opening
cutoff for historical balances. A single date field is not accepted by the export endpoint.
Ranges may cover at most 120 calendar months, counting both boundary months, regardless of
grouping or format. Longer ranges return 400 invalid_date_range before creating or queuing a job.
PDF exports additionally support at most 13 displayed periods, using the company's calendar/fiscal
report settings. Larger PDFs return 400 invalid_date_range with param end_date; use a shorter range,
coarser grouping, CSV, or XLSX instead.
group_by is an object: period controls time columns (month, quarter, or year; default month),
and rows controls row grouping (category or department; default category).
Department rows are supported only for income statements, export the GAAP department statement,
and require that feature. Omitted group_by or either member uses the corresponding default.
Company, actor, reporting-date settings, and filename are inferred by Finta. Period columns
follow the company's existing calendar/fiscal report settings; this endpoint accepts no date_type.
The worker reads the books when it generates the file, not from a frozen snapshot at submission.
Completed files are retained results; use a new Idempotency-Key for a new export of updated books.
Email delivery is not supported. Export records and their files are retained; there is no
automatic age-based cleanup for exports in the application.
Reports::GenerationRequestWorker runs on the existing critical queue.
A company may have at most five unfinished API exports across all submitting users.
Additional new exports return 429 report_export_limit_exceeded without creating or enqueueing
a job. Existing-key replays and retrieval remain available. A completed or failed export releases
its slot; retry rejected submissions after an export finishes.

Idempotency-Key is required and scoped to company, submitting user, and this operation.
Identical normalized inputs return the existing export with its current status; changed inputs
return 409 idempotency_key_conflict. The key remains reserved for the lifetime of the export.
A pending replay without a recorded enqueue repairs an interrupted dispatch; worker
executions are serialized and completed exports are not generated again. A failed export requires
a new key to retry. Creation and replay return 201 with Location pointing to the retrieval endpoint.
If enqueue acknowledgement or bookkeeping fails, creation returns 503 and retains a pending
export. Retry with the same key to recover; a job may already be queued, and duplicate deliveries
are serialized. These failures do not consume the key with a terminal enqueue failure.
The file is generated in the background; poll Location with backoff until succeeded or failed.
Every poll counts toward the usual API limits. No webhook or MCP event subscription is created.




## OpenAPI

````yaml /openapi.yaml post /reports/exports
openapi: 3.0.3
info:
  title: Finta API
  version: '1.0'
  description: >
    The Finta API provides programmatic access to your company's financial data,

    including transactions, journal entries, categories, financial reports, and
    the team directory.


    ## Authentication


    Each API key is scoped to a single company. There is no company ID
    parameter.

    The key already knows which company it belongs to. If you have access to
    multiple

    companies, create a separate key for each.


    A key acts as its owner; it does not have a separate permission snapshot.
    The owner

    must currently have **API access: Manage**, plus the permission required by
    the endpoint:


    | Endpoint | Required permission |

    |----------|---------------------|

    | `GET /company` | Company: View |

    | `GET /taxes`, `GET /taxes/{id}` | Taxes: View |

    | `POST /files`, `POST /file_uploads`, `POST /taxes/income_uploads`, `POST
    /taxes/franchise_uploads` | Taxes: Manage |

    | `GET /team` | Team & access: View |

    | `DELETE /team/invitations/{id}` | Team & access: Manage; target-management
    limits apply. No additional Growth/RBAC gate. |

    | `POST /team/invitations/{id}/resend` | Team & access: Manage;
    target-management limits apply. No additional Growth/RBAC gate. |

    | `PATCH /team/invitations/{id}` | Team & access: Manage; RBAC and
    active/trialing Growth required, with grant and target limits. |

    | `POST /team/invitations` | Team & access: Manage; requested grants must
    also be allowed for the person and plan. |

    | `GET /categories`, `GET /departments`, `GET /rules`, `GET /rules/{id}` |
    Accounting setup: View |

    | `POST /rules` | Accounting setup: Manage; applying to existing
    transactions (`apply_to_existing_transactions`, default true) also requires
    Transactions: Manage |

    | `PATCH /rules/{id}` | Accounting setup: Manage; Transactions: Manage is
    not required |

    | `DELETE /rules/{id}` | Accounting setup: Manage and Transactions: Manage,
    including absent retries |

    | `GET /integrations` | Integrations: View |

    | `GET /tax_credits`, `GET /tax_credits/{id}` | Taxes: View |

    | `GET /reports/*`, `POST /reports/exports` | Reports: View |

    | `GET /accounts`, `GET /cards`, `GET /transactions`, `GET
    /transactions/{id}` | Transactions: View |

    | `GET /forms/1099`, `GET /forms/1099/{id}` | Taxes: View |

    | `POST /transactions/bulk/update`, `GET /jobs/{id}` | Transactions: Manage
    |

    | `PATCH /transactions/{id}`, `POST /accounts`, `POST /transaction_uploads`,
    `POST /transfers`, `DELETE /transfers/{id}` | Transactions: Manage |

    | `GET /journal_entries`, `GET /journal_entries/{id}`, `GET
    /aggregations/total`, `GET /aggregations/totals`, `GET /parties/merchants` |
    Reports: View |

    | `GET /invoices`, `GET /invoices/{id}` | Invoices: View |

    | `POST /invoices`, `POST /invoices/{id}/void` | Invoices: Manage |

    | `POST /invoices/{id}/match`, `POST /invoices/{id}/unmatch` | Invoices:
    Manage and Transactions: Manage |


    Permission changes apply to the next request. Removing API access
    permanently revokes

    the owner's active keys for that company; restoring access requires creating
    a new key.


    Authenticate by including your API key in the `Authorization` header:


    ```

    Authorization: Bearer finta_...

    ```


    ### Key prefix convention


    Every Finta credential is prefixed so it can be recognized at a glance in
    logs, error messages, and secret scanners. Today there is one credential
    type and its prefix is `finta_`. As additional credential or token types are
    introduced (for example restricted keys or webhook signing secrets), each
    will have its own distinct prefix that stacks on the brand prefix (e.g.
    `finta_<type>_...`). Treat the prefix as load-bearing: do not strip it
    before sending, and do not assume a missing or unknown prefix is still a
    Finta credential.


    ## Rate Limits


    The API enforces two independent limits. Both surface as `429 Too Many
    Requests`, but they are distinct error codes with different retry guidance.


    ### Per-minute burst limit


    Authenticated requests are limited to **6,000 requests per 60 seconds** (100
    per second) per API key. Unauthenticated requests (missing or invalid key)
    are limited to **120 requests per 60 seconds** per IP address.


    The window is **fixed**, not rolling. Each 60-second wall-clock interval is
    its own counter; all requests within that interval share it, and the counter
    resets to 0 in a single step at the next boundary. Available budget jumps
    from `0` back to the full bucket size (`6000` for authenticated API keys,
    `120` for unauthenticated IP buckets) instantaneously at the boundary,
    rather than aging out one slot at a time. Trust `X-RateLimit-Reset` for the
    exact reset timestamp.


    Successful responses and burst-limit rejections include the following
    headers.

    Do not assume they are present on other errors: early authentication,
    authorization,

    and monthly-limit rejections can omit them. This describes existing header
    behavior,

    not a new quota policy.


    | Header | Description |

    |--------|-------------|

    | `X-RateLimit-Limit` | Maximum requests per window (`6000` for
    authenticated requests, `120` for unauthenticated). |

    | `X-RateLimit-Remaining` | Requests remaining in the current window. |

    | `X-RateLimit-Reset` | Unix timestamp when the window resets. |


    On a 429 for this case, an additional header is included:


    | Header | Description |

    |--------|-------------|

    | `Retry-After` | Number of seconds until the next request is safe. |


    The error body is `type: rate_limit_error` / `code: rate_limit_exceeded`.


    **Retry guidance:** This case is retryable. Honor `Retry-After` as the
    minimum wait, then apply exponential backoff with jitter for any retries
    beyond the first (cap at a few minutes; abandon after a small number of
    attempts to avoid retry storms). Do not retry tighter than `Retry-After`.


    ### Monthly per-company limit


    Each company has a monthly cap on total API calls, independent of the
    per-minute burst:


    | Plan | Monthly cap |

    |------|-------------|

    | Formation | 10 |

    | Startup | 300 |

    | Growth | 30,000 |

    | Enterprise | Unlimited |


    Enterprise is enabled internally by Finta for contract-managed companies. It
    bypasses

    commercial monthly API/MCP quotas and ordinary subscription lockouts, even
    when the

    Stripe subscription is absent or canceled. Requests and MCP tool calls are
    still counted.

    Enterprise does not bypass valid credentials, company/user permissions,
    company closure,

    request validation, payload bounds, or per-minute abuse limits. Disabling
    Enterprise restores

    ordinary plan gates using the current billing state; it does not change
    Stripe billing.


    Counts reset at **midnight Pacific Time (US & Canada)** on the 1st of each
    month. This is the Finta application's configured time zone; the cap is
    computed in that zone, not in UTC. Consumers in other time zones should
    convert the local-midnight Pacific boundary to their own clock; note that
    the absolute UTC offset shifts by one hour twice a year for daylight saving
    (PST is UTC-8, PDT is UTC-7). Blocked requests are not counted toward the
    limit. The error body is `type: limit_error` / `code:
    monthly_limit_exceeded`, and the `message` field includes the reset date in
    human-readable form.


    **Retry guidance:** This case is **not retryable in the short term.** No
    `Retry-After` header is sent, deliberately, because the only meaningful
    remediation is to upgrade the plan (Settings -> Plans at app.finta.com) or
    wait until the 1st of the next month. Naive retry loops should branch on
    `error.code` and stop retrying when they see `monthly_limit_exceeded`. SDKs
    that auto-retry on 429 should look at `error.code` (or the absence of
    `Retry-After`) before re-issuing.


    A temporary company limit override, set by Finta support for the current
    month, may change

    the allowance and the wording of the monthly-limit message. It applies to
    every user and

    API key in the company. The error code and no-short-term-retry rule remain
    unchanged.


    ## Pagination


    List endpoints use cursor-based pagination. Pass `limit` to control page
    size

    (default 100, max 500). To fetch the next page, pass `starting_after` with
    the

    `next_cursor` value from the previous response. Responses include `object:
    "list"`,

    `url` (the canonical path of the collection, relative to the API host),
    `has_more`

    (whether more results exist), `next_cursor` (an opaque value to pass
    unchanged as `starting_after`),

    and `data` (the page of items).


    ## Reporting dates and freshness


    Reports and category totals use calendar dates in **America/Los_Angeles
    (Pacific Time)**,

    including defaults such as today and the current quarter. Periodic requests
    include both

    `start_date` and `end_date` exactly. Point-in-time requests include activity
    through the

    exact `date`. Response dates identify the boundaries actually used, not
    rounded months.


    Whole months read whole-month rollups. The current month's first day through
    today in

    Pacific Time reads month-to-date (MTD) rollups when fresh. Other
    partial-month boundaries,

    including a midmonth start, read raw journal entries for just those partial
    months and are

    slower; any whole months in the same request still use rollups. Stale or
    unfilled MTD

    rollups safely fall back to raw entries. Whole-month requests and
    month-aligned

    month-start-to-today or year-to-date requests stay on the fast rollup path
    when MTD is fresh.


    Fresh MTD must end at exactly the requested cutoff. If a refresh advances
    the rollup

    across Pacific midnight during a request, exact live entries preserve the
    original

    cutoff; later-day MTD is never included in that answer.


    `refreshed_at` is a Unix timestamp covering the entire report, total, or
    grouped page,

    including partial months. For rollup-only reads it is the maximum
    contributing rollup

    timestamp, including MTD. Any live raw-entry portion is current as of the
    call, even when

    no raw entries match. A fresh MTD row contributes its timestamp even when it
    contains zero

    activity through today; future-only merchants do not appear as empty
    merchant groups.

    A grouped page covers its returned groups; a later month's refresh does not
    advance an

    earlier monthly page's timestamp.

    It is null only when no contributing timestamp or live raw read is

    available. Rollup-backed activity may lag newly imported or edited entries.


    ## Amounts


    Every monetary field in the API follows two non-negotiable rules:


    1. **Integer cents.** Monetary values are integers, never floats and never
    formatted strings. `$499.00` is `49900`. `$1,500.00` is `150000`. Divide by
    100 to get dollars. This avoids floating-point precision errors in financial
    calculations.

    2. **`_cents` suffix.** The field name always ends in `_cents` (e.g.
    `amount_cents`, `balance_cents`, `total_cents`, `net_income_cents`,
    `change_in_cash_cents`, `cash_cents`). If a field name does not end in
    `_cents`, it is not a monetary value.


    These rules apply to every monetary field across every endpoint
    (transactions, journal entries, income statement, balance sheet, cash flow).
    There are no exceptions and there is no "summary" or "display" sibling field
    that returns the same amount in dollars or as a string. If you encounter a
    field that appears to hold a monetary value but does not end in `_cents`, or
    whose value is not an integer, treat it as a bug and report it.


    Sign conventions:


    - **Income statement**: revenue/income amounts are positive, expense amounts
    are negative. Summing every section's `total_cents` yields net income
    without sign-flipping.

    - **Balance sheet**: `balance_cents` carries the natural-sign cumulative
    balance for that account at the report date.

    - **Cash flow**: `amount_cents` is positive for cash inflows and negative
    for cash outflows.


    ## Errors


    Errors return a consistent JSON structure:


    ```json

    {
      "error": {
        "type": "invalid_request_error",
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "doc_url": "https://www.finta.com/docs/api-reference/errors#resource_missing"
      }
    }

    ```


    ### Status codes


    | Status | Meaning |

    |--------|---------|

    | `200 OK` | Request succeeded. |

    | `201 Created` | A new resource was created, such as an invitation, an
    account, or a transaction upload draft. |

    | `400 Bad Request` | Request was malformed or had invalid parameters. |

    | `401 Unauthorized` | The bearer credential is missing, malformed, or
    unrecognized. The fix is to obtain a valid API key. |

    | `403 Forbidden` | The API key is valid, but the caller does not have
    permission to access the resource. The fix is to regain the required company
    or product-area access. See the `Forbidden` response component for the
    specific codes. |

    | `404 Not Found` | The requested resource, or the endpoint itself, does not
    exist. Unknown or renamed paths (for example an old hyphenated report path
    like `/reports/income-statement` instead of `/reports/income_statement`)
    return this same envelope with `type: invalid_request_error` and `code:
    resource_missing`, as JSON, regardless of the `Accept` header. |

    | `422 Unprocessable Entity` | Invitation data failed validation, for
    example an invalid email or a conflicting invitation or existing teammate. |

    | `429 Too Many Requests` | Either the per-minute burst rate limit
    (retryable, with `Retry-After`) or the monthly per-company API call limit
    (not retryable in the short term, no `Retry-After`). Branch on `error.code`
    to distinguish the two and avoid retry storms on monthly exhaustion. See the
    Rate Limits section for the full retry guidance. |

    | `5xx` | An unexpected error on Finta's side. Retry with exponential
    backoff. |


    The `error.type` and `error.code` fields are stable identifiers safe to
    switch on programmatically. The `error.message` is human-readable and may
    change without notice.


    ### Early access endpoints


    Some endpoints are in early access: documented here, but turned on per
    company while they roll

    out. For a company that doesn't have one yet, every request to it returns
    `403 Forbidden` with

    `type: permission_error` and `code: feature_disabled`, ahead of the
    endpoint's own permission

    check, and changes nothing:


    ```json

    {
      "error": {
        "type": "permission_error",
        "code": "feature_disabled",
        "message": "This feature isn’t turned on for this company yet. Contact Finta to request access.",
        "doc_url": "https://www.finta.com/docs/api-reference/errors#feature_disabled"
      }
    }

    ```


    It is not retryable: contact Finta to request access for the company, then
    retry. Once

    the feature is on for the company the endpoint works normally, with no
    change to requests.
servers:
  - url: https://app.finta.com/api/v1
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Company
    description: Retrieve company metadata
  - name: Plans & billing
    description: >-
      The plans Finta offers, what the company pays, and how much of each plan
      limit it has used.
  - name: Team
    description: Retrieve company members and manage invitations
  - name: Categories
    description: List your chart of accounts categories
  - name: Rules
    description: Company automation rule definitions.
  - name: Departments
    description: List the departments available to the authenticated company
  - name: Integrations
    description: List the authenticated company's integrations and their connection status
  - name: Aggregations
    description: >-
      Compute totals for one category and its descendants, as a single figure or
      grouped by merchant or month
  - name: Transactions
    description: >
      Transactions are what your bank shows you: a single line like "AWS charged
      you $499."

      Most API consumers want transactions. For accounting-level detail, see
      Journal Entries.


      Amounts are in cents (e.g. `amount_cents: 49900` = $499.00).
  - name: Journal Entries
    description: >
      Journal entries are the accounting records that transactions create. A
      single $499 AWS

      charge becomes two journal entries: debit Software $499, credit Cash $499
      (double-entry

      bookkeeping). Use journal entries when you need accounting-level detail.


      Amounts are in cents (e.g. `amount_cents: 49900` = $499.00).
  - name: Invoices
    description: >
      Invoices the company sent, and the bank deposits that paid them. Recording
      an invoice books

      its revenue; Finta doesn't send invoices to customers.


      Amounts are in cents (e.g. `amount_cents: 49900` = $499.00).
  - name: Parties
    description: Discover observed counterparties for category-scoped financial queries.
  - name: Forms
    description: Discover vendor compliance filing records and yearly progress.
  - name: Reports
    description: >
      Financial statements: income statement, balance sheet, and cash flow. Each
      report

      returns the complete statement for a single period. You cannot filter to
      one category

      or request multiple periods in one call.
  - name: Feedback
    description: Send questions, feedback and bug reports to Finta support
  - name: Tax Credits
    description: Credit amounts and claim progress by type, jurisdiction, and tax year
  - name: Taxes
    description: >-
      Discover income-tax and Delaware franchise-tax filing records and
      available PDFs.
  - name: Files
    description: Upload the PDFs that tax uploads attach to the company's tax records.
paths:
  /reports/exports:
    post:
      tags:
        - Reports
      summary: Create export
      description: >
        Asynchronously export an income statement, balance sheet, or cash flow
        as PDF, CSV, or XLSX.

        Requires API access: Manage and Reports: View. Does not edit company
        books; read-only OAuth

        connections may request exports. Internal read-only keys cannot POST.

        All report types require start_date and end_date. Balance sheet exports
        compare cumulative

        balances across periods in that range; start_date selects displayed
        periods, not an opening

        cutoff for historical balances. A single date field is not accepted by
        the export endpoint.

        Ranges may cover at most 120 calendar months, counting both boundary
        months, regardless of

        grouping or format. Longer ranges return 400 invalid_date_range before
        creating or queuing a job.

        PDF exports additionally support at most 13 displayed periods, using the
        company's calendar/fiscal

        report settings. Larger PDFs return 400 invalid_date_range with param
        end_date; use a shorter range,

        coarser grouping, CSV, or XLSX instead.

        group_by is an object: period controls time columns (month, quarter, or
        year; default month),

        and rows controls row grouping (category or department; default
        category).

        Department rows are supported only for income statements, export the
        GAAP department statement,

        and require that feature. Omitted group_by or either member uses the
        corresponding default.

        Company, actor, reporting-date settings, and filename are inferred by
        Finta. Period columns

        follow the company's existing calendar/fiscal report settings; this
        endpoint accepts no date_type.

        The worker reads the books when it generates the file, not from a frozen
        snapshot at submission.

        Completed files are retained results; use a new Idempotency-Key for a
        new export of updated books.

        Email delivery is not supported. Export records and their files are
        retained; there is no

        automatic age-based cleanup for exports in the application.

        Reports::GenerationRequestWorker runs on the existing critical queue.

        A company may have at most five unfinished API exports across all
        submitting users.

        Additional new exports return 429 report_export_limit_exceeded without
        creating or enqueueing

        a job. Existing-key replays and retrieval remain available. A completed
        or failed export releases

        its slot; retry rejected submissions after an export finishes.


        Idempotency-Key is required and scoped to company, submitting user, and
        this operation.

        Identical normalized inputs return the existing export with its current
        status; changed inputs

        return 409 idempotency_key_conflict. The key remains reserved for the
        lifetime of the export.

        A pending replay without a recorded enqueue repairs an interrupted
        dispatch; worker

        executions are serialized and completed exports are not generated again.
        A failed export requires

        a new key to retry. Creation and replay return 201 with Location
        pointing to the retrieval endpoint.

        If enqueue acknowledgement or bookkeeping fails, creation returns 503
        and retains a pending

        export. Retry with the same key to recover; a job may already be queued,
        and duplicate deliveries

        are serialized. These failures do not consume the key with a terminal
        enqueue failure.

        The file is generated in the background; poll Location with backoff
        until succeeded or failed.

        Every poll counts toward the usual API limits. No webhook or MCP event
        subscription is created.
      operationId: createExport
      parameters:
        - name: Idempotency-Key
          in: header
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 255
          description: >-
            Nonblank, case-sensitive key; reuse with identical inputs after an
            uncertain response.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReportExportRequest'
            examples:
              monthly_income_statement_pdf:
                summary: Monthly income statement PDF by category
                value:
                  report_type: income_statement
                  start_date: '2026-01-01'
                  end_date: '2026-09-30'
                  format: pdf
                  group_by:
                    period: month
                    rows: category
              quarterly_department_income_statement_csv:
                summary: Quarterly GAAP income statement CSV by department
                value:
                  report_type: income_statement
                  start_date: '2026-01-01'
                  end_date: '2026-09-30'
                  format: csv
                  group_by:
                    period: quarter
                    rows: department
              monthly_balance_sheet_xlsx:
                summary: Balance sheet XLSX comparing monthly balances
                value:
                  report_type: balance_sheet
                  start_date: '2026-01-01'
                  end_date: '2026-09-30'
                  format: xlsx
                  group_by:
                    period: month
                    rows: category
              annual_cash_flow_pdf:
                summary: Cash flow PDF with annual columns
                value:
                  report_type: cash_flow
                  start_date: '2024-01-01'
                  end_date: '2025-12-31'
                  format: pdf
                  group_by:
                    period: year
                    rows: category
      responses:
        '201':
          description: >-
            Export created or replayed; status may already be terminal on
            replay.
          headers:
            Location:
              schema:
                type: string
              description: Retrieval URL for this export.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReportExport'
        '400':
          description: Invalid body, field, dates, or Idempotency-Key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Authentication required.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: >-
            Insufficient permissions, read-only internal key, or unavailable
            department feature.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Idempotency key already identifies different inputs.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: >-
            API rate or monthly limit exceeded, or report_export_limit_exceeded
            when the company has five unfinished exports.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '503':
          description: report_export_failed; the export could not be accepted or queued.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  schemas:
    ReportExportRequest:
      type: object
      additionalProperties: false
      required:
        - report_type
        - start_date
        - end_date
      properties:
        report_type:
          type: string
          enum:
            - income_statement
            - balance_sheet
            - cash_flow
          description: >-
            income_statement exports profit and loss; balance_sheet compares
            cumulative balances across the requested periods; cash_flow exports
            operating, investing, and financing activity. GAAP P&L uses
            income_statement with department rows.
        format:
          type: string
          enum:
            - pdf
            - csv
            - xlsx
          default: pdf
        start_date:
          type: string
          format: date
          description: >-
            Required for all report types. For balance sheets, selects the first
            displayed period while retaining cumulative historical balances.
        end_date:
          type: string
          format: date
          description: >-
            Inclusive; required for all report types. Must be on or after
            start_date. The range may cover at most 120 calendar months,
            counting both boundary months. PDF exports additionally allow at
            most 13 displayed periods.
        group_by:
          $ref: '#/components/schemas/ReportExportGrouping'
    ReportExport:
      type: object
      required:
        - id
        - object
        - report_type
        - start_date
        - end_date
        - format
        - group_by
        - status
        - created
        - started_at
        - completed_at
        - generation_duration_ms
        - file
        - error
      properties:
        id:
          type: string
          pattern: >-
            ^export_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$
        object:
          type: string
          enum:
            - report_export
        report_type:
          type: string
          enum:
            - income_statement
            - balance_sheet
            - cash_flow
        format:
          type: string
          enum:
            - pdf
            - csv
            - xlsx
        start_date:
          type: string
          format: date
        end_date:
          type: string
          format: date
        group_by:
          allOf:
            - $ref: '#/components/schemas/ReportExportGrouping'
            - required:
                - period
                - rows
        status:
          type: string
          enum:
            - pending
            - processing
            - succeeded
            - failed
          description: >-
            pending awaits generation; processing includes generation and retry
            waits; succeeded exposes the retained file after final access checks
            while within 30 days of completed_at, then file is null; failed is
            terminal and requires a new key for a new job. An HTTP 200 retrieval
            may still carry failed status.
        created:
          type: integer
        started_at:
          type: integer
          nullable: true
          description: >-
            Unix timestamp of the first generation attempt. Null until
            generation starts, including a terminal failure when access is
            revoked before generation.
        completed_at:
          type: integer
          nullable: true
          description: >-
            Unix timestamp of terminal completion, whether succeeded or failed.
            Null while unfinished.
        generation_duration_ms:
          type: integer
          nullable: true
          minimum: 0
          description: >-
            Cumulative generation attempt duration in milliseconds, including
            upload but excluding queue and retry wait time. Null until an
            attempt finishes.
        file:
          type: object
          nullable: true
          description: >-
            Available only for succeeded exports before completed_at plus 30
            days. Null at or after that cutoff, including on idempotent POST
            replay; status remains succeeded and error remains null. Within the
            window, refresh download_url by retrieving the export again.
            Already-issued links keep their original expiry; neither link expiry
            nor the regeneration cutoff removes the stored file.
          required:
            - filename
            - content_type
            - size
            - download_url
            - expires_at
          properties:
            filename:
              type: string
            content_type:
              type: string
              enum:
                - application/pdf
                - text/csv
                - >-
                  application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
            size:
              type: integer
              minimum: 0
            download_url:
              type: string
              format: uri
            expires_at:
              type: integer
              description: >-
                Requested Unix timestamp of download link expiry, seven days
                after retrieval. Signing credentials or storage policies may
                invalidate it sooner. This does not expire the stored file.
        error:
          type: object
          nullable: true
          description: >-
            Safe job failure details, present only when status is failed. This
            is distinct from an HTTP error envelope. New submissions with
            unconfirmed dispatch return HTTP 503 and retain a pending job,
            rather than setting enqueue_failed.
          required:
            - code
            - message
          properties:
            code:
              type: string
              enum:
                - access_revoked
                - enqueue_failed
                - generation_failed
            message:
              type: string
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - type
            - code
            - message
            - doc_url
          properties:
            type:
              type: string
              enum:
                - authentication_error
                - permission_error
                - invalid_request_error
                - rate_limit_error
                - limit_error
                - api_error
              description: >-
                The error category. `authentication_error` (401) means the
                bearer credential was missing, malformed, or unrecognized.
                `permission_error` (403) means the credential was valid but the
                caller is not allowed to access the resource.
                `invalid_request_error` covers invalid parameters, unavailable
                resources, invitation validation failures, and requests that
                conflict with current state (409). `rate_limit_error` is for
                per-minute burst limits (retry shortly). `limit_error` is for
                monthly API call limits per company (upgrade or wait for reset).
                `api_error` denotes an operation failure on the server (500), or
                temporarily unavailable rule definitions (503 rules_not_ready).
                Retry with exponential backoff; persistent 503 errors require
                operator attention.
            code:
              type: string
              enum:
                - invalid_api_key
                - user_inactive
                - user_removed_from_company
                - company_closed
                - subscription_required
                - staging_access_denied
                - insufficient_permission
                - resource_missing
                - parameter_missing
                - category_update_failed
                - transaction_update_failed
                - rate_limit_exceeded
                - monthly_limit_exceeded
                - invalid_date_range
                - invalid_date
                - invalid_request
                - idempotency_key_conflict
                - plan_upgrade_required
                - feature_disabled
                - invitation_invalid
                - invitation_create_failed
                - permissions_update_failed
                - invitation_resend_failed
                - invitation_revoke_failed
                - integration_not_connectable
                - integration_already_connected
                - connect_link_in_progress
                - connect_link_failed
                - rules_not_ready
                - forms_not_ready
                - taxes_not_ready
                - rule_already_exists
                - rule_busy
                - rule_deletion_failed
                - account_create_failed
                - upload_committed
                - upload_commit_failed
                - feedback_unavailable
                - closed_books
                - invoice_already_exists
                - invoice_not_open
                - invoice_not_matched
                - payment_already_matched
                - transaction_not_matchable
                - invoice_operation_failed
                - transaction_already_matched
                - transfer_not_eligible
                - transfer_operation_failed
                - report_export_failed
                - report_export_limit_exceeded
                - invalid_file
                - wrong_document_type
                - file_already_attached
                - tax_upload_not_allowed
                - tax_upload_failed
              description: A specific error code.
            message:
              type: string
              description: A human-readable description of the error.
            doc_url:
              type: string
              format: uri
              description: A link to documentation about this error.
              example: https://www.finta.com/docs/api-reference/errors#invalid_api_key
            param:
              type: string
              nullable: true
              description: The parameter that caused the error, if applicable.
    ReportExportGrouping:
      type: object
      additionalProperties: false
      description: >-
        Omitted members default to month for period and category for rows.
        Department rows are supported only for income statements and require the
        feature.
      properties:
        period:
          type: string
          enum:
            - month
            - quarter
            - year
          default: month
          description: >-
            Time columns by month, quarter, or year. Quarter/year boundaries
            follow the company's calendar/fiscal report settings. First and last
            columns may cover partial periods.
        rows:
          type: string
          enum:
            - category
            - department
          default: category
          description: >-
            category uses accounting categories. department uses the GAAP
            department income statement; it is supported only for
            income_statement and requires the departments feature.
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: API key prefixed with `finta_`

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.