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.3.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.3-py3-none-any.whl (10.6 kB view details)

Uploaded Python 3

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

Hashes for mtl_wfirma_python_sdk-0.0.3.tar.gz
Algorithm Hash digest
SHA256 cb49690151fd087cd6f0612b1bebc2a57c27eb165e59bb5c95b9b4adb7cbcca8
MD5 15250ad0f8eca2b43aeb2514257505ce
BLAKE2b-256 c8dd1ec4544af13e84114b5da9358925b55179d2ae51788d502b6687001a8190

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for mtl_wfirma_python_sdk-0.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 ba4c8e7a68e10320ac678cad9722e3a8d6e3a548a6ee710bea62d6dd71a85257
MD5 63e0ee01a2a4eb4aed501d289e45a37a
BLAKE2b-256 76688c8d7ebc8e68a0df05438d82a6c96cd6d017b245b54a6bc94b53be422e2a

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