Skip to main content

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):

  • accessKey
  • secretKey
  • appKey
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_id if your account has multiple companies.


Modules covered

  • contractorsadd, get, edit, find
  • invoicesget, add, download, send, find (custom XML)
  • company_accountsfind, get
  • call(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 not OK (or NO_CONTENT), WFirmaAPIError is 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=xml and parses XML to Python dicts.
  • Multiple companies: set company_id in the client to avoid accidental requests to the wrong company.
  • Temporary links: /invoices/download returns a link that is valid only briefly (per backend rules).
  • Rate limits: avoid bursts; consider backoff/retries on TOTAL REQUESTS LIMIT EXCEEDED or TOTAL 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mtl_wfirma_python_sdk-0.0.6.tar.gz (8.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mtl_wfirma_python_sdk-0.0.6-py3-none-any.whl (10.6 kB view details)

Uploaded Python 3

File details

Details for the file mtl_wfirma_python_sdk-0.0.6.tar.gz.

File metadata

  • Download URL: mtl_wfirma_python_sdk-0.0.6.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

Hashes for mtl_wfirma_python_sdk-0.0.6.tar.gz
Algorithm Hash digest
SHA256 574d2b5f0b6b800723b5d353c043789c70e84e1dea6eeee1293af67000cf4319
MD5 71a073bd5340cbc7fb93186d688e3042
BLAKE2b-256 d4a569bb9fdf8b88365ecb66ed4666829f4d372ec56b237ab4f866b4debfcc6a

See more details on using hashes here.

File details

Details for the file mtl_wfirma_python_sdk-0.0.6-py3-none-any.whl.

File metadata

File hashes

Hashes for mtl_wfirma_python_sdk-0.0.6-py3-none-any.whl
Algorithm Hash digest
SHA256 64101ffab2f224fe46a112371210bc9efb4afbde640ec3543ff6aeecb4fc089a
MD5 e4efb2a2a54e2509c132bb2c1553f62b
BLAKE2b-256 a24d6835cc0ac4cda463b701fa63ac5f07b9a4cf10813be2c026262a4460b4c5

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page