OSS return validator (lines only; dispatch-country breakdown not implemented)
Every quarter, a seller registered in the Union OSS declares, per member state of consumption and per rate, the taxable amount and the VAT, plus corrections of earlier quarters. The data is already in your shop or billing export; the work is grouping it, checking each rate against the right country, and catching the rows that would make the portal or an auditor stop. POST /vat/oss/return does that in one call and returns the lines, the totals and a file. It does not file the return: you key or import it on your member state of identification’s portal.
- Input: your export as CSV (any column order, headers in English, French or German, comma or semicolon, decimal comma or point) or JSON rows. One row per invoice line or per order.
- Checks per row: member state code, rate one the country applies according to the dated table (or a table category), VAT = net × rate within rounding, supply date inside the quarter, euro amounts, supply type allowed by the scheme. Every refusal names the row and the rule.
- Output: lines per member state × supply type × rate; corrections per earlier period and member state; totals and VAT payable; due date (last day of the following month); the generic CSV.
- Cost: 25 calls per accepted return, nothing when rows are refused. The free tier (100 calls a month) covers 4 returns a month; the 2,000-call pack covers 80.
- Send an Idempotency-Key header; a retry replays the stored answer for 24 hours and is not charged again.
1. Export the quarter
Export the quarter’s B2C sales to other member states from your shop, billing tool or payment provider. The columns that matter are the country of consumption, the rate (or a category), the net amount, the VAT amount, the supply date; optional: the supply type (services or goods), the currency (must be EUR) and, for credit notes of an earlier quarter, the period they correct. Headers are matched by name, so most exports work as they are.
country;vat_rate;net;vat;invoice_date;supply_type;correction_of_period
DE;19;1 250,00;237,50;14/07/2026;goods;
DE;7;310,00;21,70;02/08/2026;goods;
FR;20;980,00;196,00;2026-09-21;services;
IT;22;-45,00;-9,90;2026-05-30;services;2026-Q22. Send it
As the raw CSV with Content-Type: text/csv and the period in the query string, or as JSON rows. scheme is union (default: EU sellers, and non-EU sellers of goods), non_union (services by sellers established outside the EU) or import (IOSS, monthly, period 2026-09). Add output=csv to receive the file alone.
curl -X POST -H "Authorization: Bearer vp_live_YOUR_KEY" -H "Content-Type: text/csv" \
--data-binary @sales-2026-Q3.csv -o oss-return-2026-Q3.csv \
"https://veriport.io/api/v1/vat/oss/return?period=2026-Q3&scheme=union&output=csv"curl -X POST -H "Authorization: Bearer vp_live_YOUR_KEY" -H "Content-Type: application/json" \
-d '{"period":"2026-Q3","scheme":"union","rows":[{"country":"DE","rate":19,"net":1250,"vat":237.5,"date":"2026-07-14","supplyType":"goods"},{"country":"FR","category":"digital_services","net":980,"date":"2026-09-21"}]}' \
"https://veriport.io/api/v1/vat/oss/return"3. Read the answer
lines[] are the return’s rows: one per member state of consumption, supply type and rate, with the number of source rows, the net, the VAT you charged and vatComputed (net × rate) for the reconciliation. corrections[] group the rows marked correctionOfPeriod per earlier period and member state: the return carries them in their own section. totals.vatPayable is the quarter’s VAT plus corrections. warnings[] flag what passed but deserves a look (a former rate before a recorded change, a line whose rounding drifts more than per-row rounding).
{
"period": {
"label": "2026-Q3",
"kind": "quarter",
"start": "2026-07-01",
"end": "2026-09-30",
"dueDate": "2026-10-31"
},
"lines": [
{
"country": "DE",
"countryName": "Germany",
"supplyType": "goods",
"rateKind": "standard",
"rate": 19,
"rows": 1,
"net": 1250,
"vat": 237.5,
"vatComputed": 237.5
},
{
"country": "DE",
"countryName": "Germany",
"supplyType": "goods",
"rateKind": "reduced",
"rate": 7,
"rows": 1,
"net": 310,
"vat": 21.7,
"vatComputed": 21.7
},
{
"country": "FR",
"countryName": "France",
"supplyType": "services",
"rateKind": "standard",
"rate": 20,
"rows": 1,
"net": 980,
"vat": 196,
"vatComputed": 196
}
],
"corrections": [
{
"period": "2026-Q2",
"country": "IT",
"countryName": "Italy",
"rows": 1,
"vat": -9.9,
"net": -45
}
],
"totals": {
"rows": 4,
"countries": 2,
"net": 2540,
"vat": 455.2,
"correctionsVat": -9.9,
"vatPayable": 445.3
}
}4. Put it on the portal — what file you get, honestly
The return is filed on the portal of your member state of identification. Where that portal publishes a file-import specification we can fetch, the endpoint will return that portal’s file; on 2026-10-24 none of the first two could be produced, so you get a clearly labelled generic CSV (files[0].portal: null, filename ending -generic.csv) that doubles as the keying sheet: one line per member state, supply type and rate, corrections after. portals[] in every answer repeats what follows.
| Member state of identification | Portal | File returned | Why |
|---|---|---|---|
| FR | impots.gouv.fr — guichet unique TVA (espace professionnel) | Generic CSV | No public file-import specification for the French OSS return was found on 2026-10-24; the return is keyed in on the portal. Use the generic CSV as the keying sheet: one line per member state, supply type and rate. |
| DE | BZStOnline-Portal (Mein BOP) — Steuererklärung One-Stop-Shop, EU-Regelung, Import-Funktion | Generic CSV | The BZSt confirms a CSV import for the OSS return and publishes the general CSV rules (UTF-8, one separator, decimal point, a #v version line) openly, but the record layout (Datensatztabelle) only inside the import help of the logged-in form, which cannot be fetched without a BOP certificate. The generic CSV follows the public rules without the version line; map its columns once against the form’s Importhilfe. |
The generic CSV is not a portal format. Germany: map its columns once to the form’s Importhilfe (visible after login) or key the lines in. France: key the lines in the guichet unique. The day a portal’s record layout becomes fetchable, a portal file is added next to the generic one and announced in the changelog.
The checks, in the order a portal would stop
- Country: an EU member state code (Greece is
EL;GRaccepted). Non-EU destinations do not belong in an OSS return. - Rate: one the member state applies according to the rates table (verified 2026-10-24, next review 2026-11-24): standard, reduced, super-reduced or parking. A category (
digital_services,ebooks…) is turned into the country’s rate for it. A rate the country no longer applies is accepted with a warning only when the supply predates the last change the table records; otherwise refused. - VAT: equals net × rate within half a cent plus 0.1 % (exports that round per line). Absent VAT is computed.
- Date: inside the quarter (or the month for IOSS); a correction’s date inside the period it corrects, which must be earlier.
- Scheme and supply type: IOSS is goods only; the non-Union scheme is services only; zero-rated or exempt supplies are refused (they are not declared through OSS).
- Totals: a member state whose quarter nets negative is refused: that is a correction of an earlier quarter and belongs in the corrections section.
- Limits: 50,000 rows or 4.5 MB per call; aggregate or split by month above that.
What the endpoint does not do
It does not convert currencies (the regulation asks for the ECB rate of the last day of the period: convert before sending, and keep the rate you used), decide whether a supply is B2C or which rate category a product falls in, check the €10,000 threshold for you, or submit anything to a tax administration. It is arithmetic and validation on your data, not tax advice; the return remains yours.
Questions
- Can I send a whole year?
- No: one return is one quarter (OSS) or one month (IOSS), and rows dated outside it are refused unless they carry the period they correct. Send one call per period.
- Are my sales stored?
- No. The rows are processed in memory to build the answer; neither the rows nor the file are stored or logged. Only the call count is recorded, as for any call.
- Why 25 calls?
- A return is a quarter’s worth of grouping and checking, not a lookup. At 25 calls it costs $0.11 from the 2,000-call pack, and the free tier (100 calls a month) produces 4 a month. It validates lines; the dispatch-country breakdown some portals ask for is not implemented.
One OSS return a month on the free tier.
Get a free keySources
- Commission Implementing Regulation (EU) 2020/194 — the OSS/IOSS VAT return: content per member state of consumption, rate type, rate, taxable amount, VAT amount, corrections (Annex III) — European Commission (EUR-Lex), consulted 2026-10-24
- One Stop Shop — guide to the VAT OSS: declare and pay, quarterly periods, corrections in a later return — European Commission, DG TAXUD, consulted 2026-10-24
- VAT e-commerce rules: One Stop Shop (OSS) and Import One Stop Shop (IOSS) — European Commission, DG TAXUD, consulted 2026-10-24
- BZSt — One-Stop-Shop, EU-Regelung: elektronische Abgabe der Steuererklärung im BZStOnline-Portal, wahlweise mit Import-Funktion — Bundeszentralamt für Steuern, consulted 2026-10-24. The page confirms a CSV import function for the OSS return; the record layout (Datensatztabelle) is published only inside the logged-in form’s import help, so this build could not fetch it. The German file is therefore the labelled generic CSV, written to the public BOP CSV rules.
- BZStOnline-Portal (Mein BOP) — Allgemeine Anleitung zur Erstellung von CSV-Dateien für die Importfunktion (UTF-8, one separator , or ;, decimal point, #v version line) — ELSTER / Bundeszentralamt für Steuern, consulted 2026-10-24
- impots.gouv.fr — guichet unique TVA (OSS/IOSS): déclaration en ligne dans l’espace professionnel — DGFiP, consulted 2026-10-24. No public file-import specification for the French OSS return was found on 2026-10-24; the French file is the labelled generic CSV until the DGFiP publishes one.
- VAT rates applied in the Member States of the European Union (situation at 1 January 2025) and the Taxes in Europe Database (TEDB) — European Commission, DG TAXUD, consulted 2026-10-24. Rates changed after the January 2025 edition (EE July 2025, RO August 2025) are taken from the national pages cited on the row and must be re-read monthly.