Symfonia WebAPI: a developer's guide — sessions, licences, the buffer and the errors you'll hit on day one
Why Symfonia Gets Its Own Guide
I've written developer guides for enova365 WebAPI, the Comarch Optima API and Sfera in Subiekt nexo, plus a comparison of all three. Symfonia was missing, and it's the one people ask about least until a project lands on their desk. Then they find out the public material is thin, the help-centre articles are behind a login, and the WebAPI behaves less like a web service than like a remote-controlled copy of Handel.
That last point explains almost everything below. Every technical claim here comes from Symfonia's own 2026 WebAPI documentation on pomoc.symfonia.pl. I cross-checked the endpoint paths against an open-source client, symf-webapi-rs, generated from the WebAPI interfaces for version 26.20. It's a third-party project, not Symfonia's, and routes do move between versions, so treat the Swagger UI on your own installation as the final word.
What the WebAPI Actually Is
Symfonia's documentation says it directly: WebAPI is a Windows service that runs Handel in the background and exposes it over HTTP(S) in the REST style, with JSON messages. It doesn't talk to the database on its own. It starts Handel's COM objects (or, on the FK side, the FK integration object) and drives them.
Six consequences follow, and the vendor states every one of them:
| Constraint | What the documentation says | What it means for you |
|---|---|---|
| Scope of one instance | one company, one Handel user | a second company means a second service, in its own directory, with its own licence |
| Handel and FK | cannot run in the same instance | integrating both programs means two services |
| Concurrent sessions | 10 by default, more on request | unclosed sessions count until they expire |
| Licence check | read only at service start | after any licence change, restart the service |
| Licence model | annual subscription, priced by integration scope | Main is required before any module |
| Parallelism | extra licences run more Handel COM instances | throughput is bought, not tuned |
Read the module list before you quote a project. On the Handel side (WAH): Main, Kontrahenci, Towary, Stany magazynowe, Zamówienia obce, Zamówienia własne, Dokumenty sprzedaży, Dokumenty zakupu, Dokumenty magazynowe, Płatności, Rezerwacje, custom modules, and Komplet. On the FK side (WAF): Main, Kontrahenci, Pracownicy, Słowniki, Rozrachunki, Wymiary, Dokumenty, Plan kont.
What about price? Symfonia doesn't publish a WebAPI figure in the documentation. Licences are annual and priced by scope. The only numbers I could find are integrator bundles: a shop connector plus a one-year WebAPI licence for Symfonia ERP Handel at 2 400 zł net a year from one vendor and 3 228 zł a year from another. That's a ceiling for the simplest case, and it includes someone else's software. Ask your Symfonia partner for the module list priced against your scope, not for "the API".
Sessions: Two Tokens, Three Kinds of 401
Authentication runs in two steps. In the configurator, each Symfonia user that WebAPI may run as gets an application token. You open a session with a GET to the session endpoint, a deviceName query parameter and this header:
GET /api/Sessions/OpenNewSession?deviceName=erp-sync HTTP/1.1
Host: symfonia-srv:9000
Authorization: Application 3f2a...-guid
The response body is the session token. Every later call carries it:
GET /api/Contractors/Page?page=1&size=100&orderBy=Asc HTTP/1.1
Authorization: Session 9c41...-guid
One honest caveat about the path. The vendor FAQ writes the call as /api/OpenNewSession, while the generated client and its controller layout use /api/Sessions/OpenNewSession (with /api/Sessions/CloseSession and /api/Sessions/SessionInformation next to it). Open Swagger on your instance and check before you hard-code either. SwaggerUI is a checkbox in the configurator, and since the 2026 release it covers every endpoint.
Session length is set per client in the configurator, and each authorised call renews it. The FAQ warns about both ends: too short and you reopen sessions constantly, too long and dead sessions sit on your 10 slots.
Here's the first real trap. According to the HTTP codes page, 401 covers an invalid application token, an invalid session token, an expired session and an exceeded session limit. Your client can't tell from the status alone whether to reopen a session or stop. The pattern I use: on 401, reopen once and retry; if the reopen itself returns 401, stop and alert, because you've either lost the token or run out of sessions.
And close sessions explicitly on shutdown. A sync job that crashes in a loop and opens a new session each time can lock everyone else out in ten iterations.
The Codes You'll Actually See
| Code | Meaning per Symfonia | What to do |
|---|---|---|
| 200 / 201 / 204 | processed; body may carry the created object | read the ID and number from the body |
| 400 | validation error or an internal Handel method failed; body has a message and ModelState | log ModelState, don't retry blindly |
| 401 | bad token, expired session, or session limit exceeded | reopen once, then alert |
| 404 | the ID doesn't exist in the database | treat as data, not as an outage |
| 409 | the object is being edited by another Handel user | back off and retry later |
| 500 | unhandled exception in WebAPI; body has the StackTrace | collect logs and report to the vendor |
The 409 deserves a note. It means someone has the contractor or document open in Handel right now. In a busy office that's not rare, so your sync has to queue the change and try again instead of failing the run.
Writing Documents: the Buffer Is the Default
Sales documents go through SalesIssue. POST /api/SalesIssue/New takes the document in the body and an issue parameter. The documentation is explicit: if you don't pass issue, the document goes to the buffer, not into the books. There's a separate PUT /api/SalesIssue/InBuffer to issue a buffered document by ID or number, and corrections go through /api/SalesIssue/NewCorrection with the same rule.
I'd keep that default. A document written by an integration should land where a person can look at it, the same principle I argued for with enova365 service accounts. The FK side works the same way: the WAF Dokumenty module adds simple, purchase, sales or custom documents to the FK buffer, and /api/FKDocuments/Buffer reads what's waiting there.
The most common failure when writing is an error like [ImportZO]: [Metoda zwróciła błąd [0].], also seen with ImportMG, ImportSP, ImportZK and ImportZW. The troubleshooting page lists the causes, and none of them is your JSON. The WebAPI user lacks permissions, has no access to some departments, warehouses or cash registers, or has no default department, warehouse or cash register set. The fix is to log into Handel once as that user, set the defaults, and try issuing the same document by hand. If Handel shows a dialog during login (an AmBasic report, an OnTimer error, an FK integration message), WebAPI won't start at all.
Reading Changes: IncrementalSync and the Unticked Box
For syncing, there's paging (/api/Contractors/Page?page=&size=&orderBy=) and there's GET /api/Contractors/IncrementalSync?dateFrom=, with equivalents for other objects. Incremental sync is what you want for a shop or a CRM: pull only what changed since the last run.
The catch is on the configurator's Synchronizacja tab. Change logging is enabled per record type, and by default only stock levels are on. Products, contractors, foreign orders, sales documents and price lists have to be switched on. If nobody does it, IncrementalSync returns an empty list and your integration looks healthy while missing every new customer. Check that tab on day one and write it into the handover document.
Two more things from the troubleshooting page I'd keep handy. After a new fiscal year is created in FK, WebAPI can fail with "Nie odnaleziono roku dla daty" until the service is restarted. And GET /api/Ping returns the loaded modules and running Handel instances, which is the fastest way to see whether a 404 on a whole controller is really a missing module licence. /api/Alive just returns the server date.
KSeF in the 2026 Release
Version 26.0.1.0 added an IKsefController: POST /api/Ksef/AddSendToKsefEvent queues a document for sending through Symfonia Cloud Connector, and ChangeKsefStatusAsync changes the KSeF status in the Handel database. The same release added attachment endpoints for sales and purchase documents. If you're building a KSeF flow for an accounting office on top of Symfonia, check the WebAPI version first. Anything before 2026 doesn't have these routes.
What I'd Build First
A read-only sync with sessions handled properly: open, renew, close on shutdown, one reopen on 401. Paging for the initial load, IncrementalSync with the right boxes ticked for everything after. Then writes into the buffer only, with a person issuing. Throughput comes last, and on Symfonia it's a licence question (more COM instances), not a code question.
The FK side is where the interesting automation is: settlements, the buffer and dimensions are exactly what an agent needs to propose postings that an accountant approves, the same split I described in the Symfonia MT940 post.
Scoping a Symfonia integration and not sure which modules you actually need? Get in touch. An hour with your configurator and your document flow settles the licence list faster than any quote.
Let’s talk about your project
Free 30-minute consultation. We’ll figure out if and how I can help.



