Click here to book a no-obligation intro call!

Docs for subscription providers with their own billing system, figures checked on 12.10.2026

How do I create subscription invoices in bulk via API in weclapp?

You create a subscription invoice via API with POST /salesInvoice after finding the customer record via GET /party or creating it with POST /party; finalizing, sending and payment matching are separate steps. The REST API is available from ERP Services (weclapp list price, as of October 2026: €86 for annual or €95 for monthly billed licenses per user per month, net). weclapp has no fixed rate limits, but it limits concurrent requests per tenant: excess requests currently wait up to 30 seconds, after which HTTP 429 follows.

View the Postman Collection for the weclapp API

What steps does a billing run have?

weclapp creates the document, your billing system knows who owes what in the month:

  1. Find or create the customer record: search in party by customerNumber, otherwise POST /party.
  2. Create the invoice: POST /salesInvoice with customerId and salesInvoiceItems.
  3. Finalize: set status via PUT /salesInvoice/id/{id}?ignoreMissingProperties=true to DOCUMENT_CREATED (invoice number, document) and to OPEN_ITEM_CREATED (open item); going from NEW directly to OPEN_ITEM_CREATED works in one request (checked in a test tenant on 12.10.2026).
  4. Send: A document email sending rule takes care of this, there is no separate endpoint.
  5. Match payment: weclapp books payments to open items, more on the page Matching subscription payments to invoices.

A final check ends the run. Overview: Accounting for SaaS, apps and subscription business.

SchrittEndpunkt / ObjektFehlerfallWiederholung
1a Datensatz suchenGET /party, Filter customerNumber-inKein oder mehrfacher TrefferLesen ist wiederholbar; Mehrfachtreffer in die Prüfliste
1b Datensatz anlegenPOST /partyHTTP 400 (Validierung)Vorher erneut suchen, sonst Dubletten
2 Rechnung anlegenPOST /salesInvoiceHTTP 400 (auch request_timeout), HTTP 429, TimeoutWarten, Backoff, dann mit der Suche nach dem Lauf-Schlüssel neu beginnen
3 AbschließenStatuswechsel per PUT /salesInvoice/id/{id}?ignoreMissingProperties=trueHTTP 400 (ohne ignoreMissingProperties=true), 403 (laut Doku im Status nicht änderbar), 409optimistic_lock: status und version neu lesen, einmal erneut; context: nicht wiederholen
4 VersendenBeleg-E-Mail-Versandregel (Einstellung)Keine Empfängeradresse, Regel greift nichtAdresse pflegen, Regel prüfen
5 Zahlung zuordnenOffener Posten, Zahlung (Seite Zahlungszuordnung)Zahlung nicht eindeutigPrüfliste, keine Zwangszuordnung
6 AbschlussprüfungGET /salesInvoice/count mit Lauf-Präfix (commission-like), gezählt je StatusAnzahl weicht ab oder Rechnungen bleiben in NEWFehlende gezielt nachholen, nie den ganzen Lauf neu starten

Numbers at a glance

€86 / €95
ERP Services per user per month (annual / monthly), includes the REST API
€55
Contract Management add-on per person per month, for recurring invoices created by weclapp itself
30 s
maximum wait time in the queue, currently, then HTTP 429
1 min
recommended minimum timeout in the client

Source: weclapp list price, as of October 2026 (tiles 1 and 2); weclapp.com/api (tiles 3 and 4).

How do you create the invoice?

The schema includes in the header, among others, customerId, invoiceDate, dueDate, termOfPaymentId, servicePeriodFrom, servicePeriodTo and commission, and per item title or articleId, quantity, unitPrice and taxId. The API docs specify: you must not send id, version, createdDate and lastModifiedDate, nonexistent fields trigger HTTP 400, and dryRun=true runs the business logic as far as possible and validates the data (200 instead of 201 on success). Check in the test tenant that nothing was created. There, the dryRun (12.10.2026) required only customerId; weclapp sets invoice date (today), tax (tenant default) and unit itself if you omit them. Additional required fields show up in the first dryRun in your tenant. The response contains neither id nor invoiceNumber and always status: NEW; the dryRun does not reflect status changes and deletion.

For the night into the first of the month: in our experience, weclapp stores a calendar date as midnight German time. If you derive the invoice date from “today in UTC”, a run shortly after midnight still produces the previous day's date, so on the first of the month the last day of the previous month. The invoice then lands in the wrong accounting period in weclapp and doesn't match the service statement. For VAT purposes in Germany, the time of supply counts, and for advance payments the receipt of the consideration (§ 13 para. 1 no. 1 lit. a UStG); in Austria, an invoice issued too late can shift the tax liability by one month (§ 19 para. 2 no. 1 UStG 1994). Derive it from the calendar day in Europe/Berlin. This is not a substitute for tax advice.

Beispielcode

Pseudocode mit dem Python-Client weclappy, nicht ausgeführt und nur für einen Testmandanten gedacht. Er nutzt ausschließlich dokumentierte Endpunkte; die Postman Collection für die weclapp API enthält sie zum Ausprobieren.

import time
from weclappy import WeclappAPIError                       # Timeout/Verbindungsabbruch: Ausgang unbekannt (ebenfalls WeclappAPIError)

WAIT = (0, 2, 5, 10, 30)   # Sekunden: Queue und Verarbeitung abwarten, bevor neu angelegt wird

def find_invoice(client, key, waits=(0,)):
    for pause in waits:
        time.sleep(pause)
        rows = client.get("salesInvoice", params={
            "commission-eq": key, "properties": "id,version,status,commission,salesInvoiceType"})
        if any(r.commission != key for r in rows):        # 1. Filter still ignoriert (falscher Operator): Lauf anhalten
            raise RuntimeError(f"Filter ignoriert, nichts anlegen: {key}")
        hits = [r for r in rows if r.status != "CANCELLED" and r.salesInvoiceType != "CREDIT_NOTE"]   # 2. Storno und Gutschrift zählen nicht
        if hits:
            return hits
    return []

def finish(client, inv):
    if inv.status == "OPEN_ITEM_CREATED":                 # schon abgeschlossen
        return inv
    return client.put(f"salesInvoice/id/{inv.id}", {"status": "OPEN_ITEM_CREATED", "version": inv.version},
                      params={"ignoreMissingProperties": "true"})   # ohne den Parameter HTTP 400; keine salesInvoiceItems mitsenden; bei 409 neu lesen

def bill_one(client, sub, run_id, customers):
    key = f"{run_id}-{sub.contract_no}"                   # Lauf-Schlüssel
    customer_id = customers.get(sub.customer_no) or create_customer(client, sub, customers)
    hits = find_invoice(client, key)
    if len(hits) > 1:
        return record_error(sub, "mehrfach", key)         # Prüfliste, nicht raten
    if hits:
        return finish(client, hits[0])                    # existiert schon: nur noch abschließen
    try:
        inv = client.post("salesInvoice", {
            "customerId": customer_id, "commission": key,
            "invoiceDate": sub.invoice_date_ms,           # Berlin-Mitternacht
            "servicePeriodFrom": sub.from_ms, "servicePeriodTo": sub.to_ms,
            "salesInvoiceItems": [{
                "title": sub.plan, "quantity": "1",
                "unitPrice": sub.net_price,               # Dezimalstring, netto, Mandantenwährung
                "manualUnitPrice": True,                  # Preis nicht aus Preisliste (laut Feldname)
                "taxId": tax_id_for(sub),                 # z. B. GET /tax/findSalesTax, mit Steuerberatung abstimmen
                "servicePeriodFrom": sub.from_ms, "servicePeriodTo": sub.to_ms}]})
        return finish(client, inv)                        # Abschluss per PUT; ein POST allein endet in NEW
    except WeclappAPIError as exc:   # Transportfehler und Timeouts gesondert behandeln: nicht wiederholen, erst suchen
        if exc.is_validation_error:                      # HTTP 400 ohne request_timeout: kann nicht angekommen sein
            return record_error(sub, type(exc).__name__, exc.detail)
        found = find_invoice(client, key, waits=WAIT)     # 3. nach Fehler erst Wartefolge, dann Suche
        if found:
            return finish(client, found[0])               # Rechnung ist angekommen: nur noch abschließen
        record_error(sub, type(exc).__name__, exc.detail) # unklar: Prüfliste, im nächsten Pass wieder mit der Suche beginnen

How do you finalize the invoice?

The public specification lists no separate endpoint for finalizing. The status field knows NEW, ENTRY_COMPLETED, DOCUMENT_CREATED, OPEN_ITEM_CREATED and CANCELLED, and PUT /salesInvoice/id/{id} accepts it; which transitions the server allows is not described in the API docs. Checked in a test tenant on 12.10.2026: with ignoreMissingProperties=true and the body {"status":"DOCUMENT_CREATED"}, weclapp assigns the final invoice number and creates the document, {"status":"OPEN_ITEM_CREATED"} additionally creates the open item (GET /salesOpenItem), also directly from NEW. dryRun=true is not suitable for this: it always rejects the status change, although the real call works. Test with a real test invoice and check status, invoice number, document and open item.

For partial updates, the docs mention ignoreMissingProperties=true and version; 409 stands for optimistic_lock, context and persistence. Without ignoreMissingProperties=true, weclapp rejected the status request in the test with 400 (statusHistory is read-only); version was optional, an outdated version resulted in 409. Do not send salesInvoiceItems along: if the id of an entry is missing in partial updates, weclapp removes it.

A POST without finalizing ends without an error message, the invoice stays in NEW, carries only a provisional number (PRES…) and, according to weclapp, has no invoice document yet. Therefore count by status at the end. If your tenant does not accept the status change, the documentation names no other way: finalize the invoices in the tenant or abort the run; we'll look at the case with you.

How does the invoice go out?

For email sending, there is no separate action on the invoice. Sending is controlled by document email sending rules (Global settings > Sales & Purchasing) that react to events such as SALES_INVOICE_DOCUMENT_CREATED. The specification does not describe the field disableRecordEmailingRule on the document; judging by its name, it switches the rule off for this document. Test this with test addresses. The rule itself can be deactivated via active, then for all documents of the tenant. Whether it triggers for invoices finalized via API is not described by weclapp.

What do you do after a timeout?

A wals.pro practice rule, not a weclapp requirement: give each invoice a run key made of run and contract, such as 2026-11-A4711, in a filterable field. After a timeout or HTTP 429, don't retry immediately, because the request may still be in the queue. Search several times with increasing intervals (for example after 2, 5, 10 and 30 seconds) and create the invoice again only if it is missing afterwards; otherwise the case goes to the review list. Canceled documents and credit notes carry their key forward (createCreditNote takes over the commission) and don't count. The commission appears as “Commission” on the invoice document, so the run key is visible to your customers; choose it so that it may appear there.

The API documentation (section Filtering) says filters on unknown or non-filterable fields are silently ignored. In a test tenant (12.10.2026) it was more nuanced: a wrong field name (comission-eq) resulted in HTTP 400, a wrong or missing operator (commission-equals, commission=…) was silently ignored and returned all invoices. The code therefore aborts if a hit doesn't match the key. Test the search in advance with two test invoices. We did not find an idempotency key for POST in the API documentation (as of 11.10.2026).

How do you correct a duplicate invoice?

That depends on the status (weclapp documentation, page changed 18.09.2023). In the status “New” or “Entry completed” (NEW and ENTRY_COMPLETED in the API), there is no invoice document yet and therefore no cancellation document; DELETE /salesInvoice/id/{id} worked in the test in NEW (HTTP 204). Don't rely on dryRun=true here: it reported 200 even for a finalized invoice, while the real call there returned 409 (cannot be deleted). From the invoice document onward, the invoice can no longer be deleted, only canceled, via API with POST /salesInvoice/id/{id}/cancel (body {}). According to weclapp, the workflow action for this exists only for the payment status “open”; you correct paid invoices via the credit note (createCreditNote), the issuer's document, not the credit note under § 14 para. 4 no. 10 UStG. Check beforehand whether a sending rule sends the cancellation document automatically. If e-invoicing is mandatory for the transaction (transactions from 2027 or 2028), the cancellation document or correction must also be issued as an e-invoice; during the transition periods, another type of invoice is still sufficient (BMF FAQ, question 7b).

You clarify a sent duplicate invoice with VAT shown with your tax advisor (§ 14c UStG, gesetze-im-internet.de, retrieved 11.10.2026). This is not a substitute for tax advice.

What do you clarify for tax purposes in advance?

An invoice under § 14 para. 4 sentence 1 UStG needs a sequential number assigned once (no. 4) and the time of supply (no. 6), for subscriptions the service period. Fewer details suffice for invoices up to €250 gross (§ 33 UStDV) and from small businesses (§ 34a UStDV); this does not apply to services with reverse charge under § 13b. The schema lists invoiceNumber as a writable field; when weclapp assigns the number from the number range is not described by weclapp. In a test tenant (12.10.2026), a new invoice initially carried only a provisional number (PRES…, its own number range); the final invoice number followed only at finalization. dryRun consumed no number, and a deleted invoice in NEW left a gap only in the provisional range. An abort between creation and finalization was not tested. Check this in your tenant and align it with your tax advisor. Don't set invoiceNumber yourself. If your billing tool still creates its own invoices, turn off creation and sending or clearly mark them as copies; there may be only one invoice with tax shown for the same service, otherwise you owe the tax shown multiple times (§ 14c UStG).

You determine the tax treatment per customer group (domestic, reverse charge, OSS) in advance with your tax advisor; a taxId hard-wired for all customers is not enough. For services to businesses in other EU states where the recipient owes the tax, you issue the invoice by the 15th of the following month, state both VAT IDs and refer to the recipient's liability for the tax (§ 14a para. 1 UStG). Your run needs its own rule for this. If GET /tax/findSalesTax finds no matching tax rule, the call returned an empty result in the test instead of an error; treat that as a case to review. The API accepts and stores servicePeriodFrom and servicePeriodTo (on header and item) as well as deliveryDate. In the standard form of a test tenant, they did not appear on the PDF; adjust your form if the service period is to be printed. Primary source: § 14 UStG, gesetze-im-internet.de, retrieved 11.10.2026. The sections cited in this part apply to Germany. For Austria, § 11 and § 19 UStG 1994 apply, more on the page Accounting for SaaS in Austria. This is not a substitute for tax advice.

What license do you need?

The REST API is part of ERP Services and ERP Trade, not the Starter. According to weclapp, recurring invoices that weclapp creates itself belong to the paid Contract Management add-on, €55 per person per month (weclapp list price, as of October 2026); for invoices your system creates via API, you don't need it. For API-only operation, one license is enough; there is no minimum number. More on format and license: E-invoicing for subscription providers.

What applies for load and rate limits?

The weclapp API has no fixed rate limits, but a limit for concurrently active requests per tenant: excess requests are not rejected but wait in a queue, and after currently up to 30 seconds, the API responds with HTTP 429. In addition, weclapp evaluates sustained high load by effective request time. Timeouts of at least one minute, backoff and the wait headers are explained on the page Does the weclapp API have a rate limit?.

Example: Monthly run with 1,200 invoices

A fictional example, a pattern and not a weclapp requirement: an app company bills 1,200 subscriptions on the first of the month. The run starts shortly after midnight and works in batches with a few parallel requests. For each invoice, search and creation follow, then finalization; errors land in a review list. At the end, it counts by status and compares the count and net totals with the target list.

You estimate the runtime like this: invoices × requests per invoice × median response time ÷ parallel requests. Assuming three requests per invoice (search, creation, one status change directly to OPEN_ITEM_CREATED), that is 3,600 requests; with two separate status changes it would be four requests and 4,800. You measure the median in the test run, excluding the wait time from X-Weclapp-Wait-Ms.

Check before the run

Icon: Key

Check the run key

Wait, search, match every hit against the key, exclude canceled documents and credit notes.

Icon: Checklist

Count by status

Invoices that stay in NEW indicate a failed finalization.

Icon: Shipping

Test sending

Check disableRecordEmailingRule and sending rules in the test tenant.

Icon: Time zone

Date in Berlin time

Derive the invoice date from the calendar day in Europe/Berlin.

When is this not a fit?

A fit

  • Your billing system knows the customer record, plan and service period.
  • You can build, operate and test a run in a test tenant.
  • You want dunning and accounting export in the same system.

Not a fit

  • weclapp should control the rhythm and renewal of subscriptions: the Contract Management add-on is intended for that.
  • You need the invoice within seconds after every payment: under load, requests wait.
  • Your tenant does not accept finalization via API, which dryRun=true doesn't show: then the run stays incomplete.
  • You will soon have to issue e-invoices to domestic business customers and can't test the format path: clarify that first. This is not a substitute for tax advice.

Frequently asked questions

Which weclapp license do I need for the API?

The REST API is available from ERP Services, not in the Starter. weclapp list price, as of October 2026: €86 (annual) or €95 (monthly) per user per month, net. For API-only operation, one license is enough; there is no minimum number. With one ERP Services license, you can issue any number of invoices.

How many invoices can I create per minute?

weclapp does not state a fixed number per minute; the API has no fixed rate limits. There is a limit for concurrently active requests per tenant, and excess requests currently wait up to 30 seconds, after which HTTP 429 follows. weclapp does not state a number for parallelism. Start with a few parallel requests and watch the wait time.

What do I do after a timeout when creating?

Don't repeat the request blindly, because the invoice may already be created or still in the queue. Search for your run key with increasing intervals and check every hit, because weclapp silently ignores filters with a wrong operator. Canceled documents and credit notes don't count. If you find nothing, the case goes to the review list. This is a wals.pro practice rule, not a weclapp requirement.

How do I send the invoices by email?

The schema has no separate action on the invoice for this. Sending is controlled by document email sending rules in the Global settings under Sales & Purchasing. Before the run, check in a test tenant which rule applies to invoices finalized via API, so that no real emails go to customers.

Can I generate XRechnung or ZUGFeRD via API?

In the public API specification (retrieved 12.10.2026), the invoice has neither a field nor an endpoint for XRechnung, ZUGFeRD or a Leitweg-ID; the only document route is the PDF download. In every package, weclapp writes e-invoices as ZUGFeRD, XRechnung and UBL; for ZUGFeRD there is a global setting for all PDF invoice documents, XRechnung runs via an optional workflow action. Whether an API-driven bulk run triggers these routes is open: the PDF of a test invoice finalized via API contained no embedded file with our test tenant's setting. Clarify this in the test tenant before the first run, we'll support you with it. The obligation to issue applies between domestic businesses for transactions performed from 01.01.2027; if you had total revenue of at most €800,000 in the previous year, it applies from 01.01.2028. Small amounts up to €250 gross and small businesses are exempt. Further exceptions are explained on the page E-invoicing for subscription providers. This is not a substitute for tax advice.

How do I test the run without burdening customers?

Start in a test tenant: weclapp offers a 30-day free trial. Set up taxes, payment terms, number range and sending rules there as in your production tenant. Besides dryRun=true, which does not reflect finalization and deletion of finalized invoices, check a real run-through with test addresses, and start the first production run with a few invoices.

How do I cancel a duplicate invoice?

That depends on the status. In the status “New” or “Entry completed”, there is no invoice document yet and therefore no cancellation document. From the invoice document onward, you can only cancel the invoice, via API with POST /salesInvoice/id/{id}/cancel; according to weclapp, you correct paid invoices via the credit note. For a sent duplicate invoice, you handle the tax treatment with your tax advisor. This is not a substitute for tax advice.

Try out the endpoints before you build the run.

The Postman Collection contains all endpoints of the weclapp API documentation and additionally undocumented ones for which weclapp gives no commitment; this page uses only documented ones. We'll walk through the run itself with you in a call.

Planning a bulk run and want to go through the pattern together?

Or write to us

Briefly tell us what this is about for you.