---
title: "List Autopost Decisions"
method: GET
path: "/companies/{company_id}/autopost-decisions"
tags: ["accounting-settings"]
---

# List Autopost Decisions

`GET /companies/{company_id}/autopost-decisions`

## Request

- Base URL: `https://app.zeno-works.ch` (the document declares no server; this is the origin it was published from)
- URL: `https://app.zeno-works.ch/companies/{company_id}/autopost-decisions`
- Auth: none declared

Recent AI-accountant autonomy decisions (CompanyMember) — the activity feed.

Shows what the autonomy policy posted (``posted``), would have posted in
``assist`` shadow mode (``shadow``), or held for human review (``held``).
``ai_no_match`` shadow rows (one per AI-consulted bank entry with no verdict)
are feed noise and hidden by default; ``include_no_match=true`` surfaces them
for audit. ``outcome`` / ``source_ref_id`` narrow server-side (ZET-75), so the
held-review surfaces can see held rows beyond the newest-``limit`` window.

Newest first, ordered ``(created_at DESC, id DESC)``. ``limit`` caps at 200 and
``offset`` walks the rest (R2-21): one fiscal year of a mid-size client makes
more decisions than one page holds, so page with ``offset += limit`` until a
call returns fewer than ``limit`` rows. The response is a bare list.

``cause`` keeps the held rows of one hold cause. An agent may accept only rows
with ``agent_acceptable`` true.

## Path parameters

- `company_id` string, required

## Query parameters

- `limit` integer
- `offset` integer
- `include_no_match` boolean
- `outcome` 'posted' | 'held' | 'shadow': Outcome recorded for one autonomy decision (``gl_autopost_decision``). ``posted`` — ``auto`` mode, policy cleared, entry was posted (AI). ``held`` — policy did not clear; nothing posted, falls back to human review. ``shadow`` — ``assist`` mode, policy *would* have posted but did not (shadow).
- `source_ref_id` string, nullable
- `cause` 'not_a_decision' | 'expense_receipt' | 'autonomy_off' | 'post_failed' | 'period_closed' | 'amount_cap' | 'credit_note' | 'duplicate_suspicion' | 'self_counterparty' | 'card_statement' | 'payroll_liability_review' | 'vat_uncertainty' | 'fx_untranslated' | 'risk_flags' | 'open_questions' | 'proposal_rejected' | 'missing_original' | 'settlement_mismatch' | 'recognition_schedule' | 'pre_llm_guard' | 'low_confidence' | 'fy_predates_books' | 'fy_gap' | 'unknown_vat_code' | 'currency_mismatch' | 'bank_history' | 'bank_suspense' | 'awaiting_review' | 'unclassified': Why a decision is sitting in the held tier.

## Headers

- `authorization` string, nullable

## Cookies

- `access_token` string, nullable

## Response `200`

Successful Response

- AutopostDecisionOut[]
  - `id` string, required
  - `source_key` string, required
  - `outcome` 'posted' | 'held' | 'shadow', required: Outcome recorded for one autonomy decision (``gl_autopost_decision``). ``posted`` — ``auto`` mode, policy cleared, entry was posted (AI). ``held`` — policy did not clear; nothing posted, falls back to human review. ``shadow`` — ``assist`` mode, policy *would* have posted but did not (shadow).
  - `acceptable` boolean
  - `agent_acceptable` boolean
  - `refusal` 'unrelated' | 'card_statement' | 'credit_note' | 'ar_reminder' | 'no_document_date', nullable
  - `reason` string, required
  - `cause` string, nullable
  - `cause_params` object, nullable
  - `effective_min_confidence` string, required
  - `confidence` string, nullable
  - `match_quality` string, nullable
  - `amount_cents` integer, nullable
  - `source_ref_type` string, nullable
  - `source_ref_id` string, nullable
  - `journal_entry_id` string, nullable
  - `journal_entry_reversed` boolean
  - `receipt_waived_by` string, nullable
  - `receipt_waived_at` string, date-time, nullable
  - `created_at` string, date-time, required
  - `accepted_by` string, nullable
  - `accepted_by_name` string, nullable
  - `accepted_at` string, date-time, nullable
  - `accepted_by_kind` 'human' | 'system' | 'ai' | 'api' | 'mcp' | 'chat': What KIND of actor posted a journal entry — the attribution flag. Two axes in one vocabulary, because the column answers one question — *can a reader take this as a person's own judgement?* **Authorship** — the value says who composed the booking: * ``ai`` = the AI-accountant composed it (an autonomous post, or a one-click accept where the content is still the accountant's proposal); * ``system`` = a mechanical/background ``SystemPrincipal`` post (migrations, close automation, deterministic seeds). **Channel** — the value says which surface a person's own action arrived through, because "a ``UserPrincipal`` acted" is NOT the same claim as "a person decided". All four are derived from the request edge by :func:`~invoiage_db.repos.gl_posting._resolve_origin`: * ``human`` = an interactive browser session (cookie/JWT auth). The only value that asserts a person was looking at the screen; * ``mcp`` = an API-key request carrying the mcp shell's ``X-Zeno-Surface`` marker — a connected agent, which MAY be relaying a person and may not; * ``api`` = any other API-key request (a script, an integration, an agent calling the REST api directly). Not provably a person either; * ``chat`` = the built-in CFO chat relaying a person's approval in-app. Its *authored* bookings stamp ``ai``; ``chat`` is only for the relay. Widening this vocabulary is a **one-way door on rollback**: the PG type is a native enum converted at load (``pg_enums._pg_enum``), so an api whose Python enum lacks a value raises ``LookupError`` reading any row that carries it. See the ``0112`` migration.
  - `proposal_kind` string, nullable
  - `receipt_route` 'card' | 'claim', nullable
  - `receipt_prefill` ReceiptPrefillOut
    - `amount` string, nullable, required
    - `date` string, nullable, required
    - `vendor` string, nullable, required
    - `is_receipt` boolean, nullable
    - `payment_method` string, nullable
    - `payment_card_last4` string, nullable
    - `paid_date` string, nullable
  - `receipt_claim` ReceiptClaimRefOut: The expense claim a receipt hold has already become (Lane B, F2778; C2b). Read-time join ``expense_claim_line.receipt_item_id`` → ``expense_claim`` - the decision itself carries no claim. Present only on a held ``expense:receipt:`` row whose document is on a claim line; the sentence "became expense claim N - approve it in Payroll → Expenses" needs exactly these three facts.
    - `claim_id` string, required
    - `claim_no` integer, required
    - `status` string, required
  - `source_label` string, nullable
  - `questions` string[], nullable
  - `advisories` string[], nullable
  - `risk_flags` RiskFlagOut[], nullable
    - `severity` string, nullable
    - `code` string, nullable
    - `message` string, nullable
  - `coding_proposal` CodingProposalOut: Typed projection of a HELD coding proposal (ZET-75). Lifted by the router only while ``outcome=held`` and the payload is a coding kind — ``code`` (invoice coder) or ``bankcode`` (bank coder) — what a reviewer needs to judge the booking (lines, dates, currency), not the raw replay payload (which stays internal and also survives on accepted rows). ``document_no`` and ``has_recognition`` are invoice-coder facts: a bank coding carries neither, so they read None/False.
    - `currency` string, nullable
    - `posting_date` string, nullable
    - `document_no` string, nullable
    - `has_recognition` boolean
    - `fx` CodingProposalFxOut: Foreign-currency marker on a held coding — the review-surface twin of :class:`~invoiage_core.accounting.reconciliation.schemas.reconciliation.CodingInferenceFx`. Both coders persist the same additive ``fx`` block. **Read ``translation`` first**: * ``converted`` (ZET-384 — the only kind written now, by both coders) — the lines are the document's (or movement's) OWN figures in ``entry_currency``, and ``CodingProposalOut.currency`` is that currency too. Accepting books them converted into ``base_currency`` at the rate published for the booking date — ``rate`` when one was known at hold time — or is refused while none is published. Nothing is ever booked at the foreign face value. * ``rate`` (ZET-203, historical invoice codings) — the lines were restated in the base currency at the rate in ``rate``; the row was held for some other reason. * ``nominal_1to1`` (R2-25, historical) — no rate was captured. On a ``bankcode`` row ``currency`` is the BASE currency (the nominal relabelled 1:1). Since ZET-384 such a row is converted on accept, never booked 1:1. Fields are optional (unlike ``CodingInferenceFx``, whose three are required) because this projects raw JSONB with no schema guarantee — the same defensive stance as :class:`CodingProposalLineOut`. A partial block still yields a caveat instead of a 500.
      - `entry_currency` string, nullable
      - `base_currency` string, nullable
      - `translation` string, nullable
      - `rate` string, nullable
    - `lines` CodingProposalLineOut[]
      - `side` string, nullable
      - `account` string, nullable
      - `vat_code` string, nullable
      - `amount_cents` integer, nullable
      - `amount_basis` string, nullable
      - `text` string, nullable
      - `new_account_name` string, nullable
    - `account` string, nullable
    - `account_reasons` string[], nullable
    - `prefill_account` string, nullable
  - `coding_recommendation` CodingRecommendationOut: The account a HELD document should most likely be coded to (ZET-386). Only a suggestion for the person coding it by hand — never what Accept & post books (that is ``coding_proposal``). ``source``: ``human_history`` = this supplier's latest coding a person decided; ``ai_history`` = its latest coding at all; ``accountant`` = the AI accountant's proposal for a receipt from a supplier with no history.
    - `account_code` string, required
    - `source` 'human_history' | 'ai_history' | 'accountant', required
  - `precedent_accounts` string[], nullable
  - `capitalization_proposal` CapitalizationProposalOut: Typed projection of a HELD fixed-asset capitalization proposal (J2). Lifted by the router only while ``outcome=held`` and ``kind=='capitalize'`` — what a reviewer needs to judge "should this purchase go into the asset register?", not the raw replay payload (which stays internal and also survives on accepted rows). ``needs_category`` is the one field the UI must act on: when true the accept body has to carry a ``fixed_asset_category_id``, because no category claims the extracted ``asset_kind`` (or several do — then ``ambiguous_category`` too, which is a settings problem rather than a document one). ``needs_split_source`` is its mixed-invoice sibling: the capital amount fits none (or several) of the coded lines, so the accept has to carry a ``reclassify_from_account_code`` chosen from ``split_source_options`` — the coded candidate account codes, deliberately not the whole chart (the accept completes a fact, it does not re-code the invoice).
    - `name` string, nullable
    - `amount_cents` integer, nullable
    - `asset_kind` string, nullable
    - `fixed_asset_category_id` string, nullable
    - `booked_account_code` string, nullable
    - `needs_category` boolean
    - `ambiguous_category` boolean
    - `needs_split_source` boolean
    - `split_source_options` string[]
  - `duplicate_of` HeldDuplicateOfOut: What a held "duplicate of already-coded document" decision matched (ZET-410). ``matched_on`` names the detector's arm — ``document_no``: the invoice number ALONE (the arm compares nothing else), described by the booking that holds it (entry, date, the holder's counterparty and gross, so a reviewer can see at a glance whether it is the same invoice); ``payment_reference``: the QR/SCOR reference, with the already-booked document (``item_id``, ``item_label``) that carries it. Every holder field is optional: a named document that no longer exists still says what the arm matched on.
    - `matched_on` 'document_no' | 'payment_reference', required
    - `document_no` string, nullable
    - `journal_entry_id` string, nullable
    - `entry_no` string, nullable
    - `entry_date` string, date, nullable
    - `counterparty_name` string, nullable
    - `gross` string, nullable
    - `currency` string, nullable
    - `item_id` string, nullable
    - `item_label` string, nullable
  - `accountant_metadata` object, nullable

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/zeno-works/apis/zeno-api.md) · [All operations](https://skmtc.dev/zeno-works/apis/zeno-api/llms.txt) · [OpenAPI document](https://skmtc.dev/zeno-works/apis/zeno-api/revisions/1eb93dde146c?raw)
