Prepare a Modelo 303 IVA filing

Use this guide when the active profile must prepare Modelo 303. Modelo 303 is the Spanish IVA/VAT self-assessment (autoliquidacion) used here to calculate standard quarterly IVA filings; monthly IVA-liquidation profiles such as REDEME or large-company taxpayers use monthly Modelo 303 periods. Voluntary SII enrolment alone remains quarterly. The registry’s official title is “Modelo 303. Impuesto sobre el Valor Anadido. Autoliquidacion.”

aeat does not submit Modelo 303 to AEAT. Export creates a local file that you upload through the official AEAT channel yourself.

The tool needs a master-key passphrase. It prompts for it interactively, or read it from AEAT_SECRET_PASSPHRASE for non-interactive runs.

The complete first-quarter chain

This is the full path from an empty store to an exported .boe for a first-period filer. Run these commands in order. Each load-bearing detail is explained below.

aeat config profile create me --quiet --tax-id 12345678Z --name "Ana" \
  --surnames "Garcia Lopez" --activity "consultoria" --activity-start-date 2026-01-01
aeat app ledger add --date 2026-02-10 --amount 1210 --direction INCOMING \
  --description "venta" --classification BUSINESS \
  --taxable-base 1000 --iva-rate 0.21 --iva-amount 210
aeat app ledger add --date 2026-02-11 --amount 605 --direction OUTGOING \
  --description "compra" --classification BUSINESS --category-id material_oficina \
  --taxable-base 500 --iva-rate 0.21 --iva-amount 105
aeat app modelo work create --modelo 303 --year 2026 --period 1T
aeat app modelo work calculate --modelo 303 --year 2026 --period 1T
aeat app modelo work verify --modelo 303 --year 2026 --period 1T
aeat app modelo export --modelo 303 --year 2026 --period 1T --output ./modelo-303.boe

Load-bearing details:

  • Create the profile with --quiet for the non-interactive form. A bare profile create me opens an interactive wizard. The profile MUST carry --name and --surnames, or export later refuses with “requires the operator name”.

  • --activity-start-date 2026-01-01 scopes the prior-period dependency out for a first period. Without it, verify blocks on the previous quarter.

  • ledger add --amount is the GROSS amount (--taxable-base + --iva-amount). Here 1000 + 210 = 1210 and 500 + 105 = 605. The tool enforces that the taxable base plus IVA equals the gross to the cent.

  • A deductible-expense row needs --category-id. List the valid ids with aeat app ledger categories. The example uses material_oficina.

  • verify reports completeness complete and granted true. export writes the .boe and reports its path, byte size, and SHA-256 checksum.

  • Casilla 65 (”% atribuible a la Administración del Estado”) resolves to 100 automatically for a común-territory profile, so casilla 66 and the headline casilla 71 (Resultado final) carry the full régimen-general result. This tool supports común-territory profiles only; foral regimes are refused at profile creation.

The rest of this guide explains each step and the checks around it.

Before you create the draft

Start with the pieces that decide whether Modelo 303 applies and which data can be calculated:

What Modelo 303 calculates

Modelo 303 calculates a period IVA self-assessment: IVA charged to customers minus deductible IVA paid, plus declared adjustments and prior-period compensation, to produce the result, payment, refund, and carry-forward casillas for the period.

In ordinary ledger-backed cases, calculation can combine:

  • IVA charged to customers (IVA repercutido) from classified income and sales rows.

  • Deductible IVA paid on purchases and expenses (IVA soportado) from classified supplier rows.

  • IVA categories, rates, directions, taxable bases, IVA amounts, business percentage, currency/FX support, and intracommunity/reverse-charge treatment recorded on ledger rows.

  • Profile facts, including IVA regime and profile-derived values that the registry binds into the form.

  • Prior Modelo 303 IVA compensation state, when the target period needs pending compensation from the previous period.

  • Explicit operator inputs, only where the registry or command help says a value cannot be derived from the profile, ledger, constants, or saved history.

Do not read that list as “every Modelo 303 box comes from the ledger.” Many casillas remain manual, profile-derived, registry-derived, or sourced from prior filing history rather than transaction rows.

Classification matters because the calculation does not guess whether a row is business, personal, mixed-use, deductible, domestic, exempt, intracommunity, or reverse-charge. Rows that are unclassified or missing required IVA fields can block calculation or produce missing binding guidance.

When you add a row by hand, pass the GROSS amount on --amount and the IVA detail explicitly:

aeat app ledger add --date 2026-02-10 --amount 1210 --direction INCOMING \
  --description "venta" --classification BUSINESS \
  --taxable-base 1000 --iva-rate 0.21 --iva-amount 210

--amount is --taxable-base plus --iva-amount, and the tool refuses the row if they do not match to the cent. A deductible-expense row also needs a --category-id; list the valid ids with aeat app ledger categories.

Create the work unit

Create or reuse the saved workspace for the active profile, modelo, filing year, period, and registry revision. This needs an active profile; create one first if you have none:

aeat config profile create me --quiet --tax-id 12345678Z --name "Ana" \
  --surnames "Garcia Lopez" --activity "consultoria" --activity-start-date 2026-01-01
aeat app modelo work create --modelo 303 --year 2026 --period 1T

The command is idempotent for the same visible target. If a work unit already exists for the active profile, Modelo 303, year, period, and resolved registry revision, aeat returns it instead of creating a duplicate.

Use the same visible target on the later commands:

aeat app modelo work status --modelo 303 --year 2026 --period 1T

For routine work, the visible target (--modelo, --year, --period) is all you need. Reference-number workflows are covered in The filing workflow: work units and calculation revisions.

Check the ledger period

The period you pass to the work unit controls the ledger window used by calculation. Calculation selects ledger rows for the requested modelo, year, and period through registry bindings and period conversion. The ledger and modelo surfaces share one grammar: pass the AEAT token with --year. For example, --year 2026 --period 1T is the first quarter; monthly token 01 with --year 2026 is January.

Check that period before calculating:

aeat app ledger preflight --year 2026 --period 1T
aeat app ledger status --year 2026 --period 1T

The row window uses the transaction operation date: raw.value_date when available, otherwise raw.booked_date. The ledger row must also be in an active lifecycle state and carry enough classification, direction, business percentage, IVA category/rate/amount, and currency/FX information for the registry binding that needs it.

The calculation path also runs ledger tax-readiness checks for registry revisions that use ledger IVA aggregation. Preflight can catch missing IVA facts, unclassified rows, non-declarable categories, unsupported currencies, and similar issues before a draft is trusted. Regimen simplificado is treated differently: those profiles provide the simplificado casillas manually instead of satisfying the ordinary IVA ledger aggregation preflight.

aeat does not silently choose a quarter from today’s date. The work unit’s --year and --period are the target.

Calculate the draft

Run calculation for the same target:

aeat app modelo work calculate --modelo 303 --year 2026 --period 1T

Calculation resolves the registry revision for that work unit, reads the active profile’s ledger for the target period, resolves profile and prior-filing bindings, runs the registry formulas, and saves a draft calculation revision. At this point the ledger slice is not frozen. The draft stores the calculated casilla values, typed observations/provenance, binding and input snapshots, and the contributing source_transaction_ids.

Re-running calculation does not edit the previous revision; it saves or reuses a content-equivalent revision and moves the work unit’s current calculation pointer.

If the command reports missing bindings or missing casillas, inspect them before adding values:

aeat app modelo bindings list --modelo 303 --year 2026 --period 1T --missing
aeat app modelo casillas 303 --period 1T --required

Only provide --binding, --casilla, --relation, or Modelo 303-specific flags when the registry/help output identifies the value you are supplying. For example, use the IVA compensation wallet commands before relying on a prior compensation amount:

aeat app modelo iva-wallet balance --as-of-year 2026
aeat app modelo iva-wallet seed --filing-year 2024 --period 4T --amount 0 --confirm

Use --amount 0 only for a true first Modelo 303 period with no previous pending IVA compensation.

Review the calculated values

List saved revisions:

aeat app modelo work revisions --modelo 303 --year 2026 --period 1T

Show the current revision’s persisted values:

aeat app modelo work revision --modelo 303 --year 2026 --period 1T

The revision view exposes the revision id and state, persisted casilla values, typed observations where available, formula ids, operands, legal/source references, source transaction ids, and, after verification, ledger snapshot and evidence fields.

For a spreadsheet review loop, see Review calculations with Google Sheets. For manual inputs, bindings, offsets, and revision selection, see Review and supply calculation inputs.

Verify and export

Verify the selected calculation:

aeat app modelo work verify --modelo 303 --year 2026 --period 1T

Verification checks the selected draft against the verified-complete contract. The report exposes the calculation revision id, completeness status, whether verification was granted or blocked, resolved and missing casillas, findings with legal/source references where available, and the next action.

On successful verification, aeat captures ledger filing snapshot and evidence over the draft’s source_transaction_ids and stores it on the verified revision. That evidence lets later staleness checks detect whether a contributing ledger row changed or disappeared. It is not a general lock on the whole ledger, and it does not freeze unrelated rows.

Export the verified or filed revision:

aeat app modelo export --modelo 303 --year 2026 --period 1T --output ./modelo-303.boe

Export writes a local AEAT-compatible fichero-BOE file and reports the output path, size, checksum, and IDs. It does not contact AEAT. For ledger-derived revisions, export expects bundled evidence or a resolvable snapshot reference; do not treat export as a way to bypass missing evidence.

If you need to mark the verified revision as filed in local history after you submit through AEAT, use the filing workflow guide:

aeat app modelo work file --modelo 303 --year 2026 --period 1T

work file is an internal local marker, not an AEAT submission.

Periods, carry-forward, and unclear cases

Modelo 303 supports quarterly and monthly period tokens in the registry. The profile determines which cadence appears in the filing calendar: ordinary non-exempt profiles are quarterly, while monthly IVA-liquidation profiles such as REDEME or large-company taxpayers are monthly. Voluntary SII enrolment by itself does not switch Modelo 303 from quarterly to monthly.

The source-backed behavior is:

  • A visible target is always profile + modelo + year + period, with the registry revision resolved from that target unless you pass an exact revision.

  • Ledger aggregation is bounded by the work unit period, not by a rolling automatic slice.

  • Prior IVA compensation is sourced from Modelo 303 previous-filing state or seeded wallet history, not guessed from the current ledger.

  • Verification/file can carry ledger snapshot and evidence for contributing rows; verification output does not print the full ledger contents, but the contents needed for evidence are preserved through snapshot/evidence records.

Invalid or unsupported period tokens are rejected, and 4T is distinct from annual periods such as 0A. What was not found in the current Modelo 303 operator surface is a Modelo 303-specific double-accounting reconciliation across periods beyond date-window filtering, source transaction id handling, import duplicate diagnostics, and finalized-revision staleness/edit guards.

If you are trying to handle an ambiguous period, a rollover between periods, or possible double accounting, do not invent a workaround in the Modelo 303 guide. Use exact work-unit or calculation-revision IDs, inspect the saved revisions, and check Troubleshooting. If the CLI does not expose the check you need, report the gap instead of changing ledger data to make the filing pass.

Next steps