A tiny Python SDK for wFirma.pl API (unofficial)
Project description
wfirma-sdk-python
Unofficial, minimal Python SDK for the wFirma.pl API (API Key & OAuth2).
Status: prototype for integrations. Before production, verify scopes, payloads and responses with official docs.
This SDK focuses on a pragmatic subset: contractors, invoices (get/add/download/send), and company accounts. It exposes a low-level
call()escape hatch for everything else.
Install
pip install wfirma-sdk-python # when published
poetry add wfirma-sdk-python
Python >= 3.9 recommended.
Quickstart
from wfirma_sdk import WFirmaAPIClient
client = WFirmaAPIClient(
base_url="https://api2.wfirma.pl",
company_id="YOUR_COMPANY_ID", # optional but recommended
# ---- Auth option A: API Key headers ----
access_key="...",
secret_key="...",
app_key="...",
# ---- OR Auth option B: OAuth2 ----
# oauth2_token="eyJhbGciOi..." # Bearer token
)
# Add contractor
resp = client.contractors.add({
"name": "Nazwa kontrahenta",
"zip": "12-345",
"country": "PL",
"tax_id_type": "custom",
"nip": "1111111111",
})
print(resp)
# Get invoice
inv = client.invoices.get(12345678)
print(inv)
# Download invoice PDF link (valid for a short time on backend)
link = client.invoices.download(
12345678,
page="all",
address=0,
leaflet=0,
duplicate=0,
payment_cashbox_documents=0,
warehouse_documents=0,
)
print(link["status"]["code"], link.get("response"))
# Send invoice by email
client.invoices.send(
12345678,
email="odbiorca@adres.pl",
subject="Otrzymałeś fakturę",
page="invoice",
leaflet=0,
duplicate=0,
body="Przesyłam fakturę",
)
Running Examples
To run the demo example:
# Clone the repository
git clone https://github.com/musictechlab/mtl-wfirma-python-sdk.git
cd mtl-wfirma-python-sdk
# Install dependencies with poetry
poetry install
# Set up your environment variables
cp .env.example .env # if available, or create .env manually
# Edit .env with your credentials:
# WFIRMA_OAUTH_TOKEN=your_oauth_token
# WFIRMA_COMPANY_ID=your_company_id
# Run the demo
poetry run python examples/demo.py
The demo will fetch the 20 most recent invoices from your wFirma account.
Authentication
API Key (headers)
Place keys in headers (client does it for you):
accessKeysecretKeyappKey
client = WFirmaAPIClient(
company_id="...",
access_key="...",
secret_key="...",
app_key="...",
)
OAuth2 (Bearer)
Use Authorization Code flow in your app to obtain an access_token. Pass it to the client:
client = WFirmaAPIClient(
company_id="...",
oauth2_token="ACCESS_TOKEN_VALUE", # adds Authorization: Bearer ... and ?oauth_version=2
)
Tip: Regardless of auth method, remember
company_idif your account has multiple companies.
Modules covered
contractors—add,get,edit,findinvoices—get,add,download,send,find(custom XML)company_accounts—find,getcall(path, ...)— low-level escape hatch
Usage
Contractors
# add
client.contractors.add({
"name": "ACME Sp. z o.o.",
"zip": "00-000",
"city": "Warszawa",
"country": "PL",
"tax_id_type": "nip",
"nip": "1234567890",
})
# get
client.contractors.get(12345)
# edit
client.contractors.edit(12345, {"name": "ACME SA", "zip": "00-001"})
# find (basic paging/fields)
client.contractors.find(page=1, limit=50, fields=["Contractor.id", "Contractor.name"])
Advanced find — pass your own <parameters> as XML:
parameters_xml = b"""<?xml version="1.0" encoding="UTF-8"?>
<api>
<contractors>
<parameters>
<page>1</page>
<limit>50</limit>
<fields>
<field>Contractor.id</field>
<field>Contractor.name</field>
</fields>
<conditions>
<condition>
<field>name</field>
<operator>like</operator>
<value>ACME</value>
</condition>
</conditions>
<order>
<asc>name</asc>
</order>
</parameters>
</contractors>
</api>"""
client.contractors.find(conditions_xml=parameters_xml)
Invoices
# get
client.invoices.get(12345678)
add — for complex nested structures use raw XML from wFirma docs:
xml_body = b"""<api>
<invoices>
<invoice>
<contractor>
<name>Testowy kontrahent</name>
<zip>10-100</zip>
<city>Wrocław</city>
<street>Prosta</street>
</contractor>
<type>correction</type>
<parent_id>16679047</parent_id>
<invoicecontents>
<invoicecontent>
<parent_id>19630727</parent_id>
<name>produkt1</name>
<count>1.0000</count>
<price>11.00</price>
</invoicecontent>
<invoicecontent>
<parent_id>19630791</parent_id>
<name>produkt2</name>
<count>1.0000</count>
<price>11.00</price>
</invoicecontent>
<invoicecontent>
<name>nowy - produkt3</name>
<count>1.0000</count>
<price>11.00</price>
</invoicecontent>
</invoicecontents>
</invoice>
</invoices>
</api>"""
client.invoices.add(invoice_xml_body=xml_body)
download — build <parameters> via helper:
client.invoices.download(
12345678,
page="all", address=0, leaflet=0, duplicate=0,
payment_cashbox_documents=0, warehouse_documents=0,
)
send
client.invoices.send(
12345678,
email="odbiorca@adresmailowy123.pl",
subject="Otrzymałeś fakturę",
page="invoice",
leaflet=0,
duplicate=0,
body="Przesyłam fakturę",
)
find — pass your own <parameters> XML for full control:
invoices_find_xml = b"""<?xml version="1.0" encoding="UTF-8"?>
<api>
<invoices>
<parameters>
<page>1</page>
<limit>20</limit>
<fields>
<field>Invoice.id</field>
<field>Invoice.fullnumber</field>
<field>Invoice.date</field>
<field>InvoiceContent.name</field>
<field>InvoiceContent.price</field>
</fields>
<conditions>
<condition>
<field>Invoice.remaining</field>
<operator>gt</operator>
<value>0</value>
</condition>
</conditions>
<order>
<desc>date</desc>
</order>
</parameters>
</invoices>
</api>"""
client.invoices.find(parameters_xml=invoices_find_xml)
Company accounts
client.company_accounts.find()
client.company_accounts.get(999)
Errors
- Network/HTTP/XML parsing errors ⇒ raises
WFirmaAPIError. - API-level status checking: if
<status><code>...</code></status>is notOK(orNO_CONTENT),WFirmaAPIErroris raised with the decoded response for inspection.
from wfirma_sdk import WFirmaAPIError
try:
client.invoices.get("bad-id")
except WFirmaAPIError as e:
print(e, e.http_status, e.response)
Tips & gotchas
- Format: SDK defaults to
inputFormat=xml&outputFormat=xmland parses XML to Python dicts. - Multiple companies: set
company_idin the client to avoid accidental requests to the wrong company. - Temporary links:
/invoices/downloadreturns a link that is valid only briefly (per backend rules). - Rate limits: avoid bursts; consider backoff/retries on
TOTAL REQUESTS LIMIT EXCEEDEDorTOTAL EXECUTION TIME LIMIT EXCEEDED. - Escape hatch:
client.call(path, method=..., params=..., body_xml=...)to hit any endpoint not yet wrapped.
Versioning
This SDK is intentionally small. Breaking changes may occur until 1.0. Pin versions in production.
Contributing
Issues / PRs welcome. Keep the scope minimal and docs-linked.
🪪 License
MIT License — © 2025 MusicTech Lab
Built with ❤️ by MusicTech Lab.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mtl_wfirma_python_sdk-0.0.3.tar.gz.
File metadata
- Download URL: mtl_wfirma_python_sdk-0.0.3.tar.gz
- Upload date:
- Size: 8.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cb49690151fd087cd6f0612b1bebc2a57c27eb165e59bb5c95b9b4adb7cbcca8
|
|
| MD5 |
15250ad0f8eca2b43aeb2514257505ce
|
|
| BLAKE2b-256 |
c8dd1ec4544af13e84114b5da9358925b55179d2ae51788d502b6687001a8190
|
File details
Details for the file mtl_wfirma_python_sdk-0.0.3-py3-none-any.whl.
File metadata
- Download URL: mtl_wfirma_python_sdk-0.0.3-py3-none-any.whl
- Upload date:
- Size: 10.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ba4c8e7a68e10320ac678cad9722e3a8d6e3a548a6ee710bea62d6dd71a85257
|
|
| MD5 |
63e0ee01a2a4eb4aed501d289e45a37a
|
|
| BLAKE2b-256 |
76688c8d7ebc8e68a0df05438d82a6c96cd6d017b245b54a6bc94b53be422e2a
|