Bulk sending from CSV

Bulk ingest via POST /v1/agent/peppol/csv. You upload a canonical CSV, each invoice is assembled into Peppol BIS 3.0 UBL and run through preflight; in send mode, every ready invoice is checked for credit and sent. Bad rows and per-invoice failures are reported in the response: never silently dropped.

This endpoint is part of the Agent API. It authenticates with the X-API-Key and X-Organization-Id headers, scope invoice:send. The supplier is always the sending organization (never taken from the CSV).

Endpoint

MethodPathScopeHeaders
POST/v1/agent/peppol/csvinvoice:sendX-API-Key, X-Organization-Id

Request body

FieldTypeRequiredDescription
csvBase64string (base64)YesBase64-encoded canonical CSV (UTF-8, BOM tolerated; separator , or ;).
modepreflight | sendYespreflight = validation only (doesn't reserve credit or send); send = validation + credit reservation + sending for every ready invoice.
autoRepairbooleanNoWhen true, normalization changes are applied and the invoice is sent; when false (default), an invoice that needs changes is rejected as needs_repair (send mode only).
POST /v1/agent/peppol/csv
X-API-Key: efk_pk_test_...
X-Organization-Id: 0a2c1f3e-…
Content-Type: application/json

{
  "csvBase64": "aW52b2ljZV9udW1iZXIsaXNzdWVfZGF0ZSxjdXJyZW5jeSwuLi4=",
  "mode": "preflight"
}

Canonical CSV

One row = one invoice line. Rows are grouped into invoices by invoice_number: an invoice with multiple lines has multiple rows with the same number (header fields repeat on every row of the group). Dates use the YYYY-MM-DD format, decimal numbers use a decimal point (e.g. 12.50).

ColumnRequiredDescription
invoice_numberYesInvoice number; rows with the same number are grouped into one invoice.
issue_dateYesIssue date (YYYY-MM-DD).
due_dateNoDue date (YYYY-MM-DD).
tax_point_dateNoDelivery date / tax point (YYYY-MM-DD).
currencyYesDocument currency (e.g. EUR).
payment_ibanNoIBAN for payment.
payment_vsNoVariable symbol.
buyer_nameYesCustomer name.
buyer_icoNoCustomer IČO.
buyer_ic_dphNoCustomer VAT ID.
buyer_peppol_idNoCustomer Peppol ID (e.g. 0245:DIČ); when provided, an SMP lookup also runs.
buyer_streetNoCustomer street.
buyer_cityNoCustomer city.
buyer_zipNoCustomer postal code.
buyer_countryNoCountry code (e.g. SK).
line_descriptionYesLine item description.
line_quantityYesQuantity (decimal with a point).
line_unitNoUnit of measure (e.g. ks, l, hod).
line_unit_priceYesUnit price (decimal with a point).
line_vat_rateYesVAT rate in % (e.g. 23).
line_discountNoLine discount.

CSV example

Invoice OF2026001 with two line items = two rows with the same invoice_number (header fields repeat):

invoice_number,issue_date,due_date,currency,payment_iban,payment_vs,buyer_name,buyer_ico,buyer_ic_dph,buyer_peppol_id,buyer_street,buyer_city,buyer_zip,buyer_country,line_description,line_quantity,line_unit,line_unit_price,line_vat_rate,line_discount
OF2026001,2026-06-22,2026-07-06,EUR,SK3112000000198742637541,2026001,Firma ABC s.r.o.,12345678,SK2012345678,0245:2012345678,Hlavná 1,Bratislava,81101,SK,Oil change,1,ks,45.00,23,0
OF2026001,2026-06-22,2026-07-06,EUR,SK3112000000198742637541,2026001,Firma ABC s.r.o.,12345678,SK2012345678,0245:2012345678,Hlavná 1,Bratislava,81101,SK,Engine oil 5W-30,4,l,12.50,23,0

Response

The response is 200 and contains summary (counts), invoices (per-invoice result), and row_errors (CSV row-level errors).

{
  "data": {
    "summary": { "total": 2, "ready_or_queued": 1, "failed": 1 },
    "invoices": [
      {
        "invoice_number": "OF2026001",
        "send_ready": true,
        "status": "queued",
        "invoice_id": "b1f0d7a2-…",
        "validation": { "ran": true, "valid": true, "error_count": 0, "warning_count": 0 },
        "repair": []
      },
      {
        "invoice_number": "OF2026002",
        "send_ready": false,
        "status": "rejected",
        "reason": "validation",
        "validation": { "ran": true, "valid": false, "error_count": 1, "warning_count": 0 },
        "repair": [
          {
            "code": "BR-CO-15",
            "message": "Invoice total with VAT must equal total without VAT plus the VAT amount.",
            "severity": "error"
          }
        ]
      }
    ],
    "row_errors": [
      { "row": 5, "column": "line_vat_rate", "message": "Invalid number (VAT rate)." }
    ]
  }
}
FieldTypeDescription
summaryobjecttotal = number of assembled invoices; ready_or_queued = ready (preflight) or queued (send); failed = failed.
invoicesarray of objectsPer-invoice result: see the table below.
row_errorsarray of objectsCSV row-level errors: row (1-based), column (optional), message.

Invoice object

FieldTypeDescription
invoice_numberstringInvoice number from the CSV.
send_readyboolean | nullPreflight verdict; null when the validator wasn't available.
statusstringSend mode only: queued | rejected | needs_repair | failed.
reasonstringRejection / failure reason: validation | needs_repair | credit | validator_unavailable | build.
invoice_idstring | nullID of the created invoice (only for queued).
validationobjectRun summary: ran, valid, error_count, warning_count.
repairarray of objectsValidation findings: field, code, message, severity.
recipientobject | nullSMP lookup result, if the CSV contained buyer_peppol_id; otherwise null.
messagestringHuman-readable message (e.g. reason for a build / credit failure).
In send mode, credit is reserved only for invoices that are send_ready: true (and don't need changes, unless autoRepair: true). An invoice without enough credit is rejected with reason: credit. Other invoices in the batch are processed independently. Test your batch in preflight mode first.

Related

To send a single document from a finished UBL/CII, use Connector. Track delivery status after sending via Receiving and statuses and Delivery evidence.