Type-Safe KSeF Integration

An Idris2 command-line client for Poland's National e-Invoice System. The authentication protocol is a compile-time state machine — call the wrong API in the wrong state and the compiler rejects your program.

data Session : SessionState -> Type where
  SUnauth     : Config -> Session Unauthenticated
  SChallenged : Config -> ChallengeData -> Session Challenged
  STokenSent  : Config -> AuthTokenData -> Session TokenSent
  SAuthed     : Config -> AccessTokenData -> Session Authenticated
  SActive     : Config -> SessionData -> Session Active
  SClosed     : Config -> Session Closed

Correctness by Construction

Dependent types turn protocol violations into compile-time errors. If it type-checks, the API contract is satisfied.

🔒

Private Session State Machine

The KSeF authentication protocol is modeled as a type-indexed state machine. Each API call consumes a session in one state and produces the next. Constructors are private — the only way to reach Authenticated is through the actual auth flow. Fabricating session states is impossible outside the module.

Compile-Time NIP Validation

Polish tax IDs are validated using an erased So proof. When the NIP literal is known at compile time, an invalid checksum is a type error. Runtime values go through a smart constructor.

data NIP : Type where
  MkNIP : (value : String)
       -> {auto 0 prf : So (isValidNIP value)}
       -> NIP
📋

KSeFNumber Format Proofs

Invoice numbers carry a So (isValidKSeFNumber value) proof: 10-digit NIP prefix, 8-digit date, code, and suffix — validated at construction. Invalid numbers from CLI args or API responses are rejected before any API call is made.

data KSeFNumber : Type where
  MkKSeFNumber : (value : String)
       -> {auto 0 prf : So (isValidKSeFNumber value)}
       -> KSeFNumber
📅

Date Format Proofs

Dates carry a So (isValidDate value) proof: YYYY-MM-DD format with month 1–12 and day 1–31 validated at construction. Invalid dates from CLI arguments or XML parsing are rejected immediately.

data Date : Type where
  MkDate : (value : String)
       -> {auto 0 prf : So (isValidDate value)}
       -> Date

Zero-FFI Architecture

HTTP goes through curl. RSA-OAEP encryption and AES-256-CBC go through openssl. No FFI, no C bindings. The exact same commands work when pasted into a terminal. Debugging is trivial.

Unified Response Handling

requireSuccess centralizes HTTP status classification across all 7 API modules. One consistent 2xx check replaces the ad-hoc mix of == 200 and >= 200 && < 300 comparisons. Success-path code only handles validated responses.

Authentication State Machine

Each state transition is a function that consumes a session in one state and produces the next. Invalid transitions are compile-time errors. Auth polling is bounded by Data.Fuel — provably terminates. All HTTP responses flow through requireSuccess for consistent 2xx classification.

Unauthenticated
getChallenge
Challenged
sendToken
TokenSent
redeemToken
Authenticated
openSession
Active
closeSession
Closed
Read-Only Path

Session Authenticated

Stops after token redemption. Supports fetchInvoiceReadOnly and queryInvoicesReadOnly. No interactive session needed — most read operations work with just an access token.

Full Session Path

Session Active

Opens an interactive session with an AES-256 symmetric key. Required for InvoiceWrite operations. Supports fetchInvoice, queryInvoices, and must be explicitly closed.

Simple Commands

Build with pack build idris-ksef and run. Each command maps directly to a KSeF API workflow.

$ ksef connectivity
KSeF API is reachable (test environment)

$ ksef auth-test
Authentication successful. JWT obtained.

$ ksef fetch invalid-number
ERROR: Invalid KSeF number format: invalid-number

$ ksef fetch 9492107026-20260316-5AD9A0000006-ED
Invoice saved to invoices/9492107026-20260316-5AD9A0000006-ED.xml

$ ksef query not-a-date 2026-03-31
ERROR: Invalid date format: not-a-date (expected YYYY-MM-DD)

$ ksef query 2026-01-01 2026-03-31
Found 12 invoice(s). Fetching...
Done. 12 invoice(s) saved to invoices/

$ ksef local list 2026-03
3 invoices found for 2026-03

$ ksef local show 9492107026-20260316-5AD9A0-ED
Invoice #FV/2026/03/001 | 2026-03-16 | Net: 1000.00 PLN
connectivity Test KSeF API reachability
auth-test Verify full authentication flow
fetch Download a single invoice by KSeF number
query Fetch all invoices in a date range
local list List cached invoices by month
local show Display full details of a cached invoice

What's Next

From invoice fetching to financial intelligence. Each phase builds on compile-time guarantees.

KSeF API v2 Client

Shipped

Full integration with Poland's National e-Invoice System.

  • Private session state machine — can't fabricate auth states
  • NIP, KSeFNumber, and Date validation with So proofs
  • Unified HTTP response classification via requireSuccess
  • Explicit error handling — no silent defaults
  • Fuel-bounded auth polling — provable termination
  • Invoice fetching and batch querying by date range
  • FA(3) XML parsing to structured domain model
  • Local invoice cache with browsing and search

Cash-to-Invoice Reconciliation

Planned

Automated matching of bank statement entries to KSeF invoices.

  • Import bank statements (MT940, CSV)
  • Match cash movements to invoices by amount, date, and counterparty NIP
  • Surface unmatched and partially matched items
  • Type-safe reconciliation ensuring completeness

Cash Flow Prediction

Planned

Forecast receivables and payables from historical invoice and payment patterns.

  • Per-counterparty payment timing analysis
  • Predict future cash inflows and outflows
  • Seasonal trend detection across invoice history
  • Cash position forecasting with confidence intervals