> ## 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 sheet

> Requires API access: Manage, Forecast: Manage and the existing sheet entitlement.
Creates blank, template or custom worksheets atomically in the authenticated company.
Blank tabs start at the UI default of 100 rows by 26 columns; templates retain their
dimensions. Values, formats and chart anchors append blank grid space within company
limits (normally 500 rows and 52 columns), preserving existing cells and formula references.
The limits of 100 worksheets, 50 charts per worksheet and a 1 MiB normalized snapshot apply.
One custom request expands to at most 500 native operations across all tabs and ranges:
each non-null cell write and each formatted cell counts, as do tab setup, charts, grid
growth and any formula-preservation writes. Null skips its cell; sparse rectangles may
span the company grid even when their area exceeds 500 cells. Template seeding is outside
this budget. Splitting ranges within the request does not reset the budget.
Validation failure saves nothing. Optional return_ranges evaluates selected outputs and
dependencies after saving; a calculation error does not undo a successful save.
Creation is not idempotent. After an ambiguous network result, check whether the document
exists before repeating the request.




## OpenAPI

````yaml /openapi.yaml post /sheets
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,
    sheets, 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 /sheets`, `GET /sheets/{id}`, `GET /sheets/{id}/cells` | Forecast:
    View |

    | `POST /sheets`, `PATCH /sheets/{id}`, `DELETE /sheets/{id}` | Forecast:
    Manage; existing sheet entitlements apply |

    | `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}`, `GET /transactions/splits`, `GET /spreads` |
    Transactions: View |

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

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

    | `PATCH /transactions/{id}`, `POST /transactions/{id}/split`, `PATCH
    /transactions/{id}/split`, `DELETE /transactions/{id}/split`, `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


    Financial resource fields follow these two rules. Generic spreadsheet cell
    arrays are

    a scoped exception, described below:


    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 monetary resource fields (transactions, journal
    entries, income statement,

    balance sheet, cash flow). There is no "summary" or "display" sibling field
    that returns

    the same financial-resource amount in dollars or as a string.


    **Spreadsheet exception:** `/sheets` range `values` are generic spreadsheet
    cells, not

    financial-resource amount fields. They preserve the existing sheet engine's
    units: for

    example, a sheet amount of $20,000 is entered as `20000`, not `2000000`.
    Formatting does

    not trigger a cents conversion. Formula results use the existing spreadsheet
    evaluator

    and FINTA behavior. Do not apply the financial-resource cents convention to
    cell arrays.


    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: Sheets
    description: >-
      Create, read, edit and delete spreadsheet documents, worksheets, cell
      ranges and embedded charts.
  - 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:
  /sheets:
    post:
      tags:
        - Sheets
      summary: Create sheet
      description: >
        Requires API access: Manage, Forecast: Manage and the existing sheet
        entitlement.

        Creates blank, template or custom worksheets atomically in the
        authenticated company.

        Blank tabs start at the UI default of 100 rows by 26 columns; templates
        retain their

        dimensions. Values, formats and chart anchors append blank grid space
        within company

        limits (normally 500 rows and 52 columns), preserving existing cells and
        formula references.

        The limits of 100 worksheets, 50 charts per worksheet and a 1 MiB
        normalized snapshot apply.

        One custom request expands to at most 500 native operations across all
        tabs and ranges:

        each non-null cell write and each formatted cell counts, as do tab
        setup, charts, grid

        growth and any formula-preservation writes. Null skips its cell; sparse
        rectangles may

        span the company grid even when their area exceeds 500 cells. Template
        seeding is outside

        this budget. Splitting ranges within the request does not reset the
        budget.

        Validation failure saves nothing. Optional return_ranges evaluates
        selected outputs and

        dependencies after saving; a calculation error does not undo a
        successful save.

        Creation is not idempotent. After an ambiguous network result, check
        whether the document

        exists before repeating the request.
      operationId: createSheet
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SheetCreateRequest'
      responses:
        '201':
          description: >-
            Saved document with worksheet/chart IDs and optional calculated
            outputs.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SheetMutationResponse'
        '400':
          description: Invalid input or a supported payload/grid limit was exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Sheet, worksheet, chart or cursor is unavailable in this company.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: >-
            Revision conflict. Reread the document and use
            error.current_revision; no operations in the conflicting batch were
            saved.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/RateLimited'
components:
  schemas:
    SheetCreateRequest:
      description: >-
        Name alone creates one blank worksheet named Sheet 1. Names must be
        nonblank. template_id and custom worksheets are mutually exclusive.
        template_options requires template_id. No company selector or grid-size
        input is accepted. return_ranges selects calculated outputs after saving
        using worksheet-name-qualified A1 addresses, up to 100 selections and
        10000 selected cells in total. Selections must fit the final grid and
        are validated before save. Omit return_ranges for metadata without
        calculation.
      oneOf:
        - $ref: '#/components/schemas/SheetBlankCreateRequest'
        - $ref: '#/components/schemas/SheetTemplateCreateRequest'
        - $ref: '#/components/schemas/SheetCustomCreateRequest'
    SheetMutationResponse:
      description: >-
        Saved document metadata with an absolute configured-app URL and stable
        worksheet/chart IDs. POST always includes worksheets and their chart
        definitions; it does not emit operation_results. Use the returned
        revision rather than assuming a fixed initial value. Optional
        return_ranges adds calculated selections and status; calculation errors
        do not roll back the saved document. Revision tracks sheet edits, not
        freshness of underlying accounting data.
      type: object
      additionalProperties: false
      properties:
        id:
          type: string
        name:
          type: string
        url:
          type: string
        revision:
          type: integer
          minimum: 0
        worksheets:
          type: array
          items:
            $ref: '#/components/schemas/SheetWorksheet'
        ranges:
          type: array
          items:
            $ref: '#/components/schemas/SheetRange'
        calculation:
          $ref: '#/components/schemas/SheetCalculation'
        operation_results:
          type: array
          items:
            $ref: '#/components/schemas/SheetOperationResult'
      required:
        - id
        - name
        - url
        - revision
    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
                - revision_conflict
                - revision_history_mismatch
                - rule_deletion_failed
                - account_create_failed
                - upload_committed
                - upload_commit_failed
                - feedback_unavailable
                - billing_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
                - transaction_already_split
                - transaction_not_splittable
                - transaction_not_split
                - split_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
            current_revision:
              type: integer
              minimum: 0
              description: >-
                Current sheet revision, returned on a revision conflict when
                available.
            param:
              type: string
              nullable: true
              description: The parameter that caused the error, if applicable.
    SheetBlankCreateRequest:
      type: object
      additionalProperties: false
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 50
        return_ranges:
          description: >-
            Optional selected results after saving. Invalid selections report
            error.param as return_ranges.
          type: array
          items:
            type: string
          maxItems: 100
      required:
        - name
    SheetTemplateCreateRequest:
      type: object
      additionalProperties: false
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 50
        template_id:
          description: >-
            income_statement, cash_flow and balance_sheet use the existing UI
            statement templates with live FINTA actuals and spreadsheet totals.
            revenue_by_customer and top_software_expenses use the last six
            completed months and accept only omitted or empty template_options.
            Prefer a statement template for standard statements; preserve live
            actuals unless the user explicitly requests a frozen snapshot.
          type: string
          enum:
            - income_statement
            - cash_flow
            - balance_sheet
            - revenue_by_customer
            - top_software_expenses
        template_options:
          $ref: '#/components/schemas/SheetTemplateOptions'
        return_ranges:
          description: >-
            Optional selected results after saving. Invalid selections report
            error.param as return_ranges.
          type: array
          items:
            type: string
          maxItems: 100
      required:
        - name
        - template_id
    SheetCustomCreateRequest:
      type: object
      additionalProperties: false
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 50
        worksheets:
          type: array
          items:
            $ref: '#/components/schemas/SheetWorksheetCreate'
          minItems: 1
          maxItems: 100
        return_ranges:
          description: >-
            Optional selected results after saving. Invalid selections report
            error.param as return_ranges.
          type: array
          items:
            type: string
          maxItems: 100
      required:
        - name
        - worksheets
    SheetWorksheet:
      type: object
      additionalProperties: false
      properties:
        id:
          type: string
        name:
          type: string
        used_range:
          type: string
          nullable: true
        row_count:
          type: integer
          minimum: 1
          description: >-
            Current grid height. Writes, formatting, copy/fill destinations and
            chart anchors automatically expand it within company limits.
        column_count:
          type: integer
          minimum: 1
          description: >-
            Current grid width. Writes, formatting, copy/fill destinations and
            chart anchors automatically expand it within company limits.
        charts:
          type: array
          items:
            $ref: '#/components/schemas/SheetChart'
      required:
        - id
        - name
        - used_range
        - row_count
        - column_count
    SheetRange:
      type: object
      additionalProperties: false
      properties:
        worksheet_id:
          type: string
        range:
          type: string
        values:
          $ref: '#/components/schemas/SheetValues'
        formulas:
          type: array
          items:
            type: array
            items:
              type: string
              nullable: true
        formats:
          type: array
          items:
            $ref: '#/components/schemas/SheetFormatRange'
      required:
        - worksheet_id
        - range
    SheetCalculation:
      type: object
      additionalProperties: false
      properties:
        status:
          type: string
          enum:
            - complete
            - error
            - unavailable
        errors:
          type: array
          items:
            $ref: '#/components/schemas/SheetCalculationError'
        warnings:
          type: array
          items:
            $ref: '#/components/schemas/SheetCalculationWarning'
      required:
        - status
        - errors
        - warnings
      description: >-
        Bounded synchronous calculation; no pending job is created. error means
        one or more formulas failed. unavailable means evaluation could not run;
        values are omitted rather than invented. Formula-error cells have null
        values and corresponding errors. A write can be saved even when this
        status is not complete.
    SheetOperationResult:
      type: object
      additionalProperties: false
      properties:
        index:
          type: integer
          minimum: 0
        type:
          type: string
          enum:
            - write_range
            - format_range
            - clear_range
            - copy_range
            - fill_range
            - insert_rows
            - delete_rows
            - insert_columns
            - delete_columns
            - add_worksheet
            - rename_worksheet
            - delete_worksheet
            - create_chart
            - update_chart
            - delete_chart
        range:
          type: string
        worksheet_id:
          type: string
        chart_id:
          type: string
      required:
        - index
        - type
    SheetTemplateOptions:
      description: >-
        Only statement templates accept nonempty options. Empty options uses the
        template defaults. Otherwise both ISO dates are required, spanning 1–13
        periods; dates must preserve an engine-supported previous period (year
        2000 or later) and end by year 9999. Invalid/reversed dates and
        selections requiring truncation are rejected. period_grouping defaults
        to month; month, quarter and year select whole calendar periods, with
        the starting date aligned to the group's beginning. These are period
        selections, not arbitrary partial-period filters.
      oneOf:
        - type: object
          additionalProperties: false
          properties: {}
          maxProperties: 0
        - type: object
          additionalProperties: false
          properties:
            starting_month:
              type: string
              format: date
            ending_month:
              type: string
              format: date
            period_grouping:
              type: string
              enum:
                - month
                - quarter
                - year
          required:
            - starting_month
            - ending_month
    SheetWorksheetCreate:
      description: >-
        Custom worksheet tab, initially 100 rows by 26 columns. Names must be
        nonblank and unique ignoring case. Ranges and formats use local A1
        selections; the existing UI grid grows as needed within company limits.
        All tabs share one 500-expanded-operation request budget, including
        worksheet setup.
      type: object
      additionalProperties: false
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 50
        ranges:
          type: array
          items:
            $ref: '#/components/schemas/SheetWriteRange'
        formats:
          type: array
          items:
            $ref: '#/components/schemas/SheetFormatRange'
        charts:
          type: array
          items:
            $ref: '#/components/schemas/SheetChartCreate'
          maxItems: 50
      required:
        - name
    SheetChart:
      type: object
      additionalProperties: false
      properties:
        id:
          type: string
        type:
          type: string
          enum:
            - bar
            - stacked_bar
            - horizontal_bar
            - line
            - area
            - stacked_area
            - pie
        source_range:
          type: string
        source_ranges:
          type: array
          items:
            type: string
        series_orientation:
          type: string
          enum:
            - columns
            - rows
        anchor_cell:
          type: string
          description: >-
            Automatically appends blank rows or columns to fit this anchor,
            within company sheet limits. Invalid anchor selections report
            error.param as chart.anchor_cell.
          default: A1
        width:
          type: number
          default: 408
          minimum: 0
          exclusiveMinimum: true
        height:
          type: number
          default: 240
          minimum: 0
          exclusiveMinimum: true
        offset_x:
          type: number
          default: 0
        offset_y:
          type: number
          default: 0
        title:
          type: string
        x_axis_title:
          type: string
        y_axis_title:
          type: string
        show_title:
          type: boolean
        show_x_axis_title:
          type: boolean
        show_x_axis_labels:
          type: boolean
        show_y_axis_title:
          type: boolean
        show_y_axis_labels:
          type: boolean
        show_data_labels:
          type: boolean
        show_legend:
          type: boolean
        series:
          type: array
          items:
            $ref: '#/components/schemas/SheetChartSeries'
      required:
        - id
        - type
        - source_range
    SheetValues:
      type: array
      items:
        type: array
        items:
          $ref: '#/components/schemas/SheetCellValue'
      description: >-
        Rectangular row-major matrix exactly matching the inclusive selected
        range. Write values must be strings, booleans, finite numbers or null.
        Null positions are retained in the matrix but skip cell-write
        operations. Sparse write rectangles can exceed 500 cells in area within
        company grid limits; the aggregate expanded-operation budget still
        applies across the entire request.
    SheetFormatRange:
      type: object
      additionalProperties: false
      properties:
        range:
          type: string
        format:
          $ref: '#/components/schemas/SheetFormat'
      required:
        - range
        - format
    SheetCalculationError:
      type: object
      additionalProperties: false
      properties:
        worksheet_id:
          type: string
        cell:
          type: string
        code:
          type: string
        message:
          type: string
        formula:
          type: string
      required:
        - code
        - message
    SheetCalculationWarning:
      type: object
      additionalProperties: false
      properties:
        code:
          type: string
        formula:
          type: string
        count:
          type: integer
          minimum: 0
        message:
          type: string
      required:
        - code
        - message
    SheetWriteRange:
      type: object
      additionalProperties: false
      properties:
        range:
          type: string
        values:
          $ref: '#/components/schemas/SheetValues'
      required:
        - range
        - values
    SheetChartCreate:
      type: object
      additionalProperties: false
      properties:
        type:
          type: string
          enum:
            - bar
            - stacked_bar
            - horizontal_bar
            - line
            - area
            - stacked_area
            - pie
        source_range:
          type: string
        series_orientation:
          type: string
          enum:
            - columns
            - rows
        anchor_cell:
          type: string
          description: >-
            Automatically appends blank rows or columns to fit this anchor,
            within company sheet limits. Invalid anchor selections report
            error.param as chart.anchor_cell.
          default: A1
        width:
          type: number
          default: 408
          minimum: 0
          exclusiveMinimum: true
        height:
          type: number
          default: 240
          minimum: 0
          exclusiveMinimum: true
        offset_x:
          type: number
          default: 0
        offset_y:
          type: number
          default: 0
        title:
          type: string
        x_axis_title:
          type: string
        y_axis_title:
          type: string
        show_title:
          type: boolean
        show_x_axis_title:
          type: boolean
        show_x_axis_labels:
          type: boolean
        show_y_axis_title:
          type: boolean
        show_y_axis_labels:
          type: boolean
        show_data_labels:
          type: boolean
        show_legend:
          type: boolean
      required:
        - type
        - source_range
      description: >-
        Chart embedded in the target worksheet using the existing chart engine,
        not an exported image. Source ranges and anchor are local A1 addresses.
        The source must fit the resulting worksheet grid; only the anchor
        requests automatic expansion. series_orientation defaults to columns
        (series arranged by columns); rows arranges series by rows. Width and
        height are positive and at most 10000; offsets are between -10000 and
        10000. Geometry supports at most two decimal places. Chart text fields
        are at most 10000 characters. Unsupported values are rejected.
    SheetChartSeries:
      type: object
      additionalProperties: false
      required:
        - name
        - source_range
      properties:
        name:
          type: string
        color:
          type: string
          enum:
            - blue
            - yellow
            - green
            - purple
            - orange
            - pink
            - teal
            - coral
        name_range:
          type: string
        source_range:
          type: string
        category_range:
          type: string
        category_cells:
          type: array
          items:
            type: string
            nullable: true
    SheetCellValue:
      oneOf:
        - type: string
          nullable: true
        - type: number
        - type: boolean
      description: >-
        Spreadsheet cell value in worksheet units, not an automatic cents
        conversion. On writes, null skips a cell and an empty string clears its
        contents; strings beginning with = are formulas. No RAW mode or new
        literal escaping is introduced. Input is stored through the existing
        text-based engine, not as a separate persisted JSON scalar type. In
        calculated output, blank and formula-error cells may be null; consult
        calculation.errors to distinguish failures. Authored whitespace-only
        strings remain text, not numeric zero.
    SheetFormat:
      type: object
      additionalProperties: false
      properties:
        align:
          type: string
          enum:
            - left
            - center
            - right
        auto_align:
          type: string
          enum:
            - right
        background_color:
          type: string
        bold:
          type: boolean
        borders:
          type: object
          additionalProperties: false
          properties:
            top:
              type: string
            right:
              type: string
            bottom:
              type: string
            left:
              type: string
        decimals:
          type: integer
          minimum: 0
          maximum: 10
        font_size:
          type: string
          enum:
            - heading
            - default
            - subtext
        indent:
          type: integer
          minimum: 0
          maximum: 10
        italic:
          type: boolean
        number_style:
          type: string
          enum:
            - number
            - accounting
            - date
            - period
            - percent
        reference_kind:
          type: string
          enum:
            - department
        text_color:
          type: string
      description: >-
        Existing spreadsheet formatting in snake_case. Partial updates preserve
        unspecified properties. Period and department-reference formatting
        carries semantic meaning, not only presentation. Accounting uses
        worksheet units without cents conversion; percent displays a fractional
        rate as a percentage. Colors use the existing product color validator.
        Each formatted cell consumes one operation; format selections expand the
        grid within company limits.
  responses:
    Unauthorized:
      description: >-
        Authentication failed. Returned when the bearer credential itself is
        missing, malformed, or unrecognized. The consumer should obtain a valid
        API key and retry. This is distinct from `403 Forbidden`, which is
        returned when the credential is valid but the caller is not allowed to
        access the resource.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              type: authentication_error
              code: invalid_api_key
              message: >-
                Invalid API key provided. Check that your API key is correct and
                active.
              doc_url: https://www.finta.com/docs/api-reference/errors#invalid_api_key
    Forbidden:
      description: >-
        The API key was successfully authenticated, but the caller is not
        allowed to access this resource. The fix is not "get a new key" (that
        returns 401) but "regain access" (reactivate the user, restore company
        access, restore an active subscription, or ask an administrator for the
        required product permission). These codes can be returned, distinguished
        by `error.code`:
          - `user_inactive` - the user that owns the API key has been deactivated.
          - `user_removed_from_company` - the user no longer has access to the company the key was created for.
          - `company_closed` - the company is closed, closing, or deleted.
          - `subscription_required` - the company does not have an active subscription.
          - `staging_access_denied` - the staging environment is restricted to Finta users.
          - `insufficient_permission` - the key owner lacks API access or the endpoint's required area permission.
          - `feature_disabled` - the endpoint is in early access and not yet turned on for this company; contact Finta to request access. See "Early access endpoints" under Errors.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            user_inactive:
              summary: User has been deactivated
              value:
                error:
                  type: permission_error
                  code: user_inactive
                  message: >-
                    The user associated with this API key is no longer active.
                    Reactivate the user or create a new API key under an active
                    user.
                  doc_url: >-
                    https://www.finta.com/docs/api-reference/errors#user_inactive
            user_removed_from_company:
              summary: User no longer has access to this company
              value:
                error:
                  type: permission_error
                  code: user_removed_from_company
                  message: >-
                    The user associated with this API key no longer has access
                    to this company. Ask a company admin to re-grant access,
                    then create a new API key.
                  doc_url: >-
                    https://www.finta.com/docs/api-reference/errors#user_removed_from_company
            company_closed:
              summary: Company is closed
              value:
                error:
                  type: permission_error
                  code: company_closed
                  message: >-
                    This company is closed. The API is not available for closed
                    companies.
                  doc_url: >-
                    https://www.finta.com/docs/api-reference/errors#company_closed
            subscription_required:
              summary: Company subscription is not active
              value:
                error:
                  type: permission_error
                  code: subscription_required
                  message: >-
                    This company does not have an active subscription. Visit
                    Settings -> Plans at app.finta.com to resolve, then retry.
                  doc_url: >-
                    https://www.finta.com/docs/api-reference/errors#subscription_required
            staging_access_denied:
              summary: Staging access is restricted
              value:
                error:
                  type: permission_error
                  code: staging_access_denied
                  message: This staging environment is restricted to Finta users.
                  doc_url: >-
                    https://www.finta.com/docs/api-reference/errors#staging_access_denied
            insufficient_permission:
              summary: Key owner lacks the required product permission
              value:
                error:
                  type: permission_error
                  code: insufficient_permission
                  message: >-
                    The API key owner needs the Transactions: View permission to
                    use this endpoint.
                  doc_url: >-
                    https://www.finta.com/docs/api-reference/errors#insufficient_permission
            feature_disabled:
              summary: Endpoint is in early access and not yet on for this company
              value:
                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
    RateLimited:
      description: >-
        Too many requests. Two distinct limits can trigger a 429: (1) Per-minute
        burst limit (6,000 requests per 60 seconds per API key) returns
        `rate_limit_error` / `rate_limit_exceeded` with `Retry-After` header.
        (2) Monthly API call limit per company returns `limit_error` /
        `monthly_limit_exceeded` with the reset date in the message. Monthly
        limits vary by plan: Formation (10/month), Startup (300/month), Growth
        (30,000/month). Enterprise bypasses commercial monthly quotas, but still
        counts requests and enforces per-minute abuse limits. Blocked requests
        do not count toward the limit.
      headers:
        Retry-After:
          schema:
            type: integer
          description: >-
            Seconds until the burst rate limit resets. Only present for
            rate_limit_error responses.
        X-RateLimit-Limit:
          schema:
            type: integer
          description: >-
            Maximum requests per burst window. Only present for rate_limit_error
            responses.
        X-RateLimit-Remaining:
          schema:
            type: integer
          description: >-
            Requests remaining in burst window (0 when rate limited). Only
            present for rate_limit_error responses.
        X-RateLimit-Reset:
          schema:
            type: integer
          description: >-
            Unix timestamp when the burst window resets. Only present for
            rate_limit_error responses.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            burst_rate_limit:
              summary: Per-minute burst rate limit exceeded
              value:
                error:
                  type: rate_limit_error
                  code: rate_limit_exceeded
                  message: >-
                    Too many requests. Please retry after the Retry-After
                    period.
                  doc_url: >-
                    https://www.finta.com/docs/api-reference/errors#rate_limit_exceeded
            monthly_limit:
              summary: Monthly API call limit exceeded
              value:
                error:
                  type: limit_error
                  code: monthly_limit_exceeded
                  message: >-
                    Monthly API limit reached. Your plan allows 300 calls per
                    company per month. Resets on April 1, 2026. Upgrade in
                    Settings -> Plans at app.finta.com.
                  doc_url: >-
                    https://www.finta.com/docs/api-reference/errors#monthly_limit_exceeded
  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.