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 want | Read |
|---|---|
| The supplier document as received: header, line items, tax breakdown, payment terms | GET /invoices, GET /invoices/{code} |
| The accounting view: debit/credit lines, G/L accounts, cost centers | GET /journal-entries, GET /invoices/{code}/journal-entries |
| What was handed to the customer's financial system, and when | GET /fi-export/jobs, GET /fi-export/jobs/{job_code}, and on a DATEV tenant GET /invoices/{code}/datev-submissions |
| Card and bank transactions, for reconciliation | POST /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 areYYYY-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_UPLOADorINBOUND_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
codeyou saw. Codes are stable. - For a change feed, use the audit log with
object_type=invoiceand astart_datewindow. 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 (onlylimit,offset,search, andsort_byovercreated_at,transaction_date,total_amountorcode), so sort bytransaction_dateand select on the entry's ownstatusandposted_atfields, which move once and never again.statusisPENDING,POSTED,FAILEDorCANCELED.
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.