---
title: "Get Invoice"
method: GET
path: "/v2/ap-portals/invoices/{ap_portal_invoice_id}"
tags: ["ap-portals"]
---

# Get Invoice

`GET /v2/ap-portals/invoices/{ap_portal_invoice_id}`

## Request

- Base URL: `https://api.stuut.ai` (the document declares no server; this is the origin it was published from)
- URL: `https://api.stuut.ai/v2/ap-portals/invoices/{ap_portal_invoice_id}`
- Auth: HTTP bearer

Get swept invoice

One swept invoice with its portal lines, tax groups, payments, typed totals, and the PO
it bills against (a query-time PO join, not a stored link). Detail-sweep fields are null
on a list-only sweep. For what would be submitted, use ``submit-preview`` instead.

## Path parameters

- `ap_portal_invoice_id` string, required

## Response `200`

Successful Response

- APPortalInvoiceDetail: One swept invoice with its lines, tax groups, payments, and the PO it bills against. Typed totals come from the detail sweep and are null on a list-only sweep. For the ERP-invoice-vs-PO-line comparison used before submitting, use ``submit-preview``.
  - `id` string, required
  - `invoice_number` string, nullable, required
  - `canonical_status` 'not_submitted' | 'submitting' | 'submitted' | 'approved' | 'scheduled' | 'paid' | 'rejected' | 'disputed' | 'on_hold' | 'cancelled' | 'unknown', required: Canonical, MECE invoice status normalized across portals — the dashboard's at-a-glance state. Derived from the raw portal status (preferred) with the coarse ``SubmissionState`` as fallback (see stuut.core.ap_portal.normalize). Portal facts only: submit pre-flight results live on the separate ``submit_gate`` axis.
  - `state` 'submitted' | 'approved' | 'rejected' | 'pending', required: Coarse, normalized invoice lifecycle from a sweep/submit.
  - `invoice_status` string, nullable, required
  - `routing_status` string, nullable, required
  - `customer` string, nullable, required: Portal buyer identifier.
  - `reference` string, nullable, required
  - `amount` string, nullable, required
  - `currency` string, nullable, required
  - `invoice_date` string, date, nullable, required
  - `po_reference` string, nullable, required
  - `reject_reason` 'po_mismatch' | 'missing_required_field' | 'invalid_tax' | 'duplicate_invoice' | 'unknown_customer' | 'invalid_date' | 'transmission_failed' | 'not_stated' | 'other' | 'po_line_mismatch' | 'receipt_pending' | 'disallowed_charge' | 'invoice_data_error' | 'prerequisite_document' | 'wrong_channel' | 'po_closed', required: Closed reject taxonomy — remediation routes by code, not string-match. Buckets group rejections by what fixes them: missing fields/attachments into ``MISSING_REQUIRED_FIELD``; tax errors and missing tax docs into ``INVALID_TAX``.
  - `reject_reason_text` string, nullable, required
  - `submit_gate` 'ok' | 'needs_review' | 'blocked', required: Submit pre-flight gate for one invoice — can we submit it right now? Computed from the field-mapping + PO/order checks (see ``stuut.core.ap_portal.preview``) and persisted so the dashboard can flag "needs a fix before submit" without re-running a preview. A separate axis from ``APPortalInvoiceStatus`` (what the portal reports about the invoice).
  - `submit_gate_reason` string, nullable, required
  - `payload_id` string, nullable, required
  - `provider` 'kehe' | 'taulia' | 'coupa' | 'ariba' | 'meijer' | 'kroger' | 'walgreens' | 'albertsons' | 'target' | 'heb' | 'heb_sidm' | 'c_and_s' | 'walmart' | 'walmart_highradius' | 'cvs' | 'cvs_sap' | 'cvs_image_silo' | 'kroger_supplier_connect' | 'kroger_claimtrax' | 'kroger_verigon' | 'servicetitan' | 'sandbox' | 'generic', required: Supported AP Portal providers.
  - `ap_portal_account_id` string, required
  - `invoice_id` string, nullable, required
  - `synced_at` string, date-time, required
  - `net_total` string, decimal, nullable, required
  - `tax_total` string, decimal, nullable, required
  - `gross_total` string, decimal, nullable, required
  - `paid` boolean, required
  - `paid_amount` string, decimal, nullable, required
  - `payment_status` string, nullable, required
  - `payment_term` string, nullable, required
  - `payment_date` string, date, nullable, required
  - `due_date` string, date, nullable, required
  - `status_changed_at` string, date-time, nullable, required
  - `lines` APPortalInvoiceLineSchema[], required
    - `id` string, required
    - `line_number` string, nullable, required
    - `kind` 'quantity' | 'amount', required: Line pricing model — quantity-based (qty x unit price) or amount-based (flat total).
    - `description` string, nullable, required
    - `supplier_part_number` string, nullable, required
    - `uom` string, nullable, required
    - `quantity` string, decimal, nullable, required
    - `unit_price` string, decimal, nullable, required
    - `line_total` string, decimal, nullable, required
    - `currency` string, nullable, required
    - `po_number` string, nullable, required
    - `po_line_number` string, nullable, required
    - `po_line_reference` string, nullable, required
    - `service_sheet_line` string, nullable, required
  - `tax_lines` APPortalInvoiceTaxLineSchema[], required
    - `id` string, required
    - `amount` string, decimal, nullable, required
    - `rate` string, decimal, nullable, required
    - `code` string, nullable, required
    - `display_code` string, nullable, required
    - `display_rate` string, nullable, required
    - `rate_description` string, nullable, required
  - `payments` APPortalInvoicePaymentSchema[], required
    - `id` string, required
    - `amount_paid` string, decimal, nullable, required
    - `payment_date` string, date, nullable, required
    - `channel` string, nullable, required
    - `reference` string, nullable, required
    - `portal_payment_id` string, nullable, required
  - `related_order` APPortalRelatedOrderItem, required: The PO a swept invoice bills against, with its budget totals.
    - `id` string, required
    - `order_number` string, required
    - `state` 'open' | 'on_hold' | 'changed' | 'invoiced' | 'closed' | 'cancelled' | 'not_found' | 'unknown', required: Normalized purchase-order lifecycle from a sweep. Invoice creation is a separate fact (``can_create_invoice``), not answered by these labels: OPEN (active), ON_HOLD (buyer reviewing), CHANGED (superseded — re-fetch the new PO), INVOICED (fully billed), CLOSED / CANCELLED (terminal), NOT_FOUND (referenced but not located in the portal), UNKNOWN (unrecognized provider status). The "PO running low" headroom signal is derived from amounts, not from a status.
    - `currency` string, nullable, required
    - `amount_total` string, decimal, nullable, required
    - `amount_invoiced_total` string, decimal, nullable, required

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/stuut/apis/stuut-api.md) - [All operations](https://skmtc.dev/stuut/apis/stuut-api/llms.txt) - [OpenAPI document](https://skmtc.dev/stuut/apis/stuut-api/revisions/ff0ad16a55ff?raw)
