Skip to main content

Pull invoice data

Reading spend out of SpendOne: invoices and their line items, the booking that was proposed for each, the journal entries behind them, and the export jobs that carried them into the ledger. This is the feed for a data warehouse, a spend-analytics tool, or a reconciliation job on your side.

Where the numbers actually live​

Three levels, and picking the wrong one is the usual mistake:

You wantRead
The supplier document as received: header, line items, tax breakdown, payment termsGET /invoices, GET /invoices/{code}
The accounting view: debit/credit lines, G/L accounts, cost centersGET /journal-entries, GET /invoices/{code}/journal-entries
What was handed to the customer's financial system, and whenGET /fi-export/jobs, GET /fi-export/jobs/{job_code}, and on a DATEV tenant GET /invoices/{code}/datev-submissions
Card and bank transactions, for reconciliationPOST /transaction-events/export

Listing invoices​

curl -G https://api.spendone.tech/api/v1/invoices \
-H "Authorization: Bearer $TOKEN" \
--data-urlencode "limit=100" \
--data-urlencode "offset=0"

The list takes limit, offset and search (fuzzy over code and invoice_number). Standard envelope and pagination, as in API Basics.

Two statuses, two questions​

An invoice carries status and workflow_status, and they move independently. Branching on the wrong one is the second usual mistake.

status is the extraction pipeline: PENDING_EXTRACTION on upload, PROCESSING while extraction and enrichment run, SAVED once they finished, ERROR on a pipeline fault, DUPLICATE for a document matched to an invoice already on file. DUPLICATE is terminal: it never reaches SAVED, generates no journal entry, and is left out of cumulative reconciliation. Exclude it from any sum.

workflow_status is the business process, in order: RECEIVED → PO_ASSIGNMENT_PENDING (only when no purchase order matched automatically) → TAX_VALIDATION_PENDING → PO_RECONCILIATION_PENDING → ADDITIONAL_INFO_PENDING → READY_FOR_EXPORT_PREPARATION → READY_FOR_DATEV → ACCOUNTANT_COMPLETE. FI_TRANSFERRED is reached only from READY_FOR_DATEV, once the export has handed the invoice over. CANCELED and EXTERNAL are the other ends, EXTERNAL meaning the document is not SpendOne business. REJECTED_BY_ACCOUNTANT sits off the path and is not an end: the buyer resolves it, and resuming re-enters at TAX_VALIDATION_PENDING.

For a warehouse, FI_TRANSFERRED and ACCOUNTANT_COMPLETE are the states that mean the numbers are final. Anything earlier can still change.

The enums are published, so treat an unrecognized value as unknown rather than as an error.

Fields worth knowing​

  • invoice_number, invoice_issue_date, payment_due_date: the document's own identity and dates. Business dates are YYYY-MM-DD; timestamps are RFC 3339.
  • invoice_lines, vat_breakdowns, document_totals, document_allowances, document_charges: the money, in the e-invoicing shape.
  • early_payment_deadline, early_payment_discount_percentage (and the _2_ pair): Skonto.
  • purchase_order_reference, purchase_order_id: the order it was matched to.
  • seller, supplier_id: the extracted seller block, and the supplier in your master data it resolved to. They are not the same thing: extraction proposes, a human confirms.
  • ingestion_channel: MANUAL_UPLOAD or INBOUND_EMAIL, derived at read time rather than stored.
  • paid, sent_to_accounting: the two flags most reporting actually keys on.
  • einvoice_compliant, einvoice_format, specification_identifier: set where the document arrived as structured e-invoice XML rather than a PDF.

The booking​

The booking proposal is the G/L coding SpendOne suggests before anything is posted: GET /invoices/{code}/booking-proposal. It is a proposal, and it is editable (PUT) until the invoice leaves the workflow.

The journal entry is the committed booking:

GET /api/v1/journal-entries # all, paginated
GET /api/v1/journal-entries/{code}
GET /api/v1/invoices/{code}/journal-entries # the entries for one invoice

An entry carries status (PENDING, POSTED, FAILED, CANCELED), posted_at, total_amount, currency, transaction_date, performance_date, due_date, the legal entity, and its lines. Only POSTED entries are in the ledger; a PENDING one is still editable through POST/PUT/DELETE /journal-entries/{code}/lines[/{id}], and POST /journal-entries/{code}/post is what commits it.

GET /journal-entries/airplus-fx-fees lists the AirPlus FX and card-fee collective bookings, which have no invoice behind them and would otherwise be missing from a sum built purely from invoices.

What reached the ledger​

GET /api/v1/fi-export/jobs # every export job
GET /api/v1/fi-export/jobs/{job_code} # one job with its submissions
GET /api/v1/fi-export/jobs/{job_code}/download-url # a signed URL for the file
GET /api/v1/fi-export/jobs/{job_code}/sap-download-url # the SAP variant

One job answers for many invoices: GET /api/v1/fi-export/jobs/{job_code} carries a submissions array with one entry per invoice, each with its invoice_code, status and error_message, next to the job's own successful_items / failed_items counters. On the DATEV method an invoice can also be submitted and tracked individually: GET /invoices/{code}/datev-submissions, GET /invoices/{code}/datev-submission/latest, GET /datev/submissions/{id}. Full mechanics in Accounting System Integration.

Card and bank transactions​

There is deliberately no paginated list of card transactions or statement lines. The supported bulk read is an export job:

POST /api/v1/transaction-events/export # start it
GET /api/v1/transaction-events/export/{job_code} # poll
GET /api/v1/transaction-events/export/{job_code}/download # fetch the result

GET /transaction-events/transactions/{identifier}/events gives the event stream for one transaction, and GET /airplus/statements/accrual-report is the month-end aggregate over ingested statements.

Pulling incrementally​

There is no updated_since filter on the invoice list, so pick the pattern that matches what you need:

  • For a full snapshot, page the list and hold the last code you saw. Codes are stable.
  • For a change feed, use the audit log with object_type=invoice and a start_date window. It tells you which invoices changed and what changed on them; fetch those by code.
  • For the accounting numbers only, GET /journal-entries. It takes no status or date filter (only limit, offset, search, and sort_by over created_at, transaction_date, total_amount or code), so sort by transaction_date and select on the entry's own status and posted_at fields, which move once and never again. status is PENDING, POSTED, FAILED or CANCELED.

Back off on 429, honoring Retry-After when it is present.

Permissions​

Reading invoices, journal entries and the accounting export requires ACCOUNTANT. ADMIN does not carry it: since the roles were split, elevated business access sits with the accountant role, not the administrator. See Roles and Permissions.