Portuguese Fiscalization (SAFT-PT)

We provide a complete mechanism for SAFT-PT compliance based on the Portuguese Tax Authority's (Autoridade Tributária e Aduaneira - AT) regulatory requirements.

It is required by law for all companies operating in Portugal to maintain SAFT-PT compliant records of all invoices, credit notes, and other fiscal documents. Space Invoices automatically generates SAFT-PT compliant data, document hashes, QR codes, and ATCUD codes.

WARNING: While we take great care to ensure SAFT-PT compliance and that our technical solution fits all requirements where applicable, we urge users to do their own research on the matter. Space Invoices can only ensure the technical aspect of the process, the final responsibility of compliance setup and operation is on the side of the user / provider of the data / the issuer of the invoices ie. the end user and not Space Invoices.

Organization Requirements

The organization must be configured with all required fields for SAFT-PT compliance. These are validated when creating or updating a Portuguese organization.

Field Format Notes
countryAlpha2Code PT Must be Portugal
taxNumber 9 digits Valid Portuguese NIF, cannot be 123456789
companyNumber string Commercial registry number, or NIF if no commercial registration
phone international format e.g. +351 123456789
email valid email Organization contact email
address string Latin characters only
city string Latin characters only
zip XXXX-XXX Portuguese postal code format (e.g. 1000-001)
state string Region name (used to determine fiscal region)
startingCapital number Company starting capital amount

The fiscalRegionCode is automatically determined from the state field:

  • Azores → PT-AC
  • Madeira → PT-MD
  • All others → PT

NOTE: Once documents have been created, taxNumber and countryAlpha2Code cannot be changed.

Account Requirements

The following fields are required for Portuguese document validation. They are resolved in this order:

  1. From _at.accountFirstname, _at.accountLastname, _at.accountTaxNumber on the document (if provided)
  2. Fallback to firstname, lastname, taxNumber on the Account making the request

The resolved values are saved to the document's _at object automatically on each save.

Account field _at override Format Notes
firstname _at.accountFirstname string Latin characters only
lastname _at.accountLastname string Latin characters only
taxNumber _at.accountTaxNumber string Personal tax identification number

Set account-level defaults via the Account API, or pass per-document values in _at when creating the document.

      
        // Option A: set on the account (applies to all documents)
const response = await fetch('https://api.spaceinvoices.com/v1/accounts/{id}', {
  method: 'PATCH',
  headers: {
    "Authorization": "ACCESS_TOKEN",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "firstname": "Jo\u00e3o",
    "lastname": "Silva",
    "taxNumber": "123456789"
  })
});

// Option B: pass per-document via _at (overrides account values)
const document = await fetch('https://api.spaceinvoices.com/v1/organizations/{id}/documents', {
  method: 'POST',
  headers: {
    "Authorization": "ACCESS_TOKEN",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "type": "invoice",
    "_at": {
      "accountFirstname": "Jo\u00e3o",
      "accountLastname": "Silva",
      "accountTaxNumber": "123456789"
    },
    // ... other document fields
  })
});      
    

Your access token is displayed in examples.

        
          # Option A: set on the account (applies to all documents)
curl -X PATCH 'https://api.spaceinvoices.com/v1/accounts/{id}' \
  -H 'Authorization: ACCESS_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "firstname": "Jo\u00e3o",
    "lastname": "Silva",
    "taxNumber": "123456789"
  }'

# Option B: pass per-document via _at (overrides account values)
curl -X POST 'https://api.spaceinvoices.com/v1/organizations/{id}/documents' \
  -H 'Authorization: ACCESS_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "type": "invoice",
    "_at": {
      "accountFirstname": "Jo\u00e3o",
      "accountLastname": "Silva",
      "accountTaxNumber": "123456789"
    }
  }'        
      

Your access token displayed in examples.

ATCUD Series

Before issuing documents, you must register ATCUD series obtained from the Portuguese Tax Authority (AT).

Field Format Notes
validationCode string (min 8 chars) Code from AT, alphanumeric without vowels, 0, or 1
code string Series identifier (e.g. MYSERIES)
documentType enum invoice, credit-note, or estimate
startDate date When the series becomes valid
firstNumber string Starting sequential number

Supported Document Types

Type SAFT-PT Code Category
invoice FT (Fatura) Sales Invoice
advance FT (Fatura) Sales Invoice
credit-note NC (Nota de Crédito) Sales Invoice
estimate OR (Working Document) Working Document

Document numbers are automatically generated in the format: {TYPE} {SERIES}/{SEQUENCE}

Examples: FT MYSERIES/000000001, NC MYSERIES/000000042

Document Item Classification (required)

Each non-separator document item must include a classification field. This field is required for Portuguese organizations. Valid values:

Value SAFT-PT ProductType Use for
product P Physical goods
service S Services
advance O Advance payments
convenience_fee I Convenience fees
late_fee I Late payment fees
handling_fee I Handling fees

NOTE: Use the API value (e.g. service), not the SAFT-PT code (e.g. S).

NOTE: For advance documents, all items must have classification set to advance.

  
    // Example document item
{
  "name": "Consulting service",
  "quantity": 1,
  "priceGross": 100.00,
  "classification": "service",
  "_documentItemTaxes": [{ "rate": 23.0 }]
}  

Document Client Requirements

The _documentClient object has conditional requirements based on the isEndCustomer flag:

End customer (isEndCustomer: true)

Field Required Notes
isEndCustomer YES Must be true
name NO Latin characters only
taxNumber NO Not required for end customers

Business customer (isEndCustomer: false or omitted)

Field Required Format
isEndCustomer YES Must be false
address YES Latin characters only
city YES Latin characters only
zip YES XXXX-XXX (Portuguese postal code)
taxNumber YES 9 digits (valid Portuguese NIF)

Tax Requirements

Taxes are matched by rate in _documentItemTaxes. For Portuguese documents, a legal 0% line must resolve to explicit exemption metadata. A missing tax is not the same as a valid Portuguese zero-rate tax.

The API resolves Portuguese zero-rate metadata in this order:

  1. _documentItems[*]._documentItemTaxes[*].taxExemptionCode and taxExemptionReason
  2. the referenced Tax record's taxExemptionCode and taxExemptionReason

If a Portuguese line resolves to 0% and no exemption metadata can be resolved, the API rejects the document.

The export mapping for the most common Portuguese cases is:

Case Input SAFT-PT output
Standard VAT positive VAT rate NOR, RED, or INT depending on rate/classification
Margin scheme rate: 0 + M13 TaxType=IVA, TaxCode=OUT
Intra-community delivery rate: 0 + M16 TaxType=IVA, TaxCode=ISE
Export delivery rate: 0 + M02 TaxType=IVA, TaxCode=ISE
Not subject to VAT rate: 0 + M99 TaxType=NS, TaxCode=NS

Tax exemption codes: When a tax rate is 0 or exempt, a taxExemptionCode should be provided. Valid Portuguese codes include M01, M02, M04, M05, M06, M07, M09, M10, M11, M12, M13, M14, M15, M16, M19, M20, M21, M25, M26, M30, M31, M32, M33, M34, M40, M41, M42, M43, M44, M45, M46, and M99.

The special code M99 indicates "Not Subject to VAT".

PT 0% Tax Metadata

Use an explicit 0% tax whenever a Portuguese document line is legally non-taxable or exempt. Do not leave the line without tax and expect the export to infer the legal reason later.

You can implement this in two supported ways:

  • Create dedicated Tax records with rate: 0, taxExemptionCode, and taxExemptionReason, then reference them by taxId.
  • Pass line-level _documentItemTaxes with rate: 0, taxExemptionCode, and taxExemptionReason when the meaning is document-specific.

Line-level exemption metadata overrides the referenced tax.

Recommended dedicated Portuguese zero-rate taxes:

  • M13 for margin scheme / second-hand goods
  • M16 for intra-community delivery
  • M02 for export delivery
  • M99 only when the transaction is not subject to VAT

Important: A generic "NoTax" tax should not be reused for multiple Portuguese legal cases. Create one zero-rate tax per legal meaning, or pass the exemption data on the document line.

Typical validation errors are:

  • Portuguese 0% taxes require taxExemptionCode metadata.
  • Portuguese 0% taxes require taxExemptionReason metadata.
  • PT 0% tax metadata missing for line "...". No tax is attached; provide taxExemptionCode/taxExemptionReason on the document line or referenced tax.
  
// Option A: create a dedicated PT 0% tax and reuse it
await fetch('https://api.spaceinvoices.com/v1/Organizations/{id}/taxes', {
  method: 'POST',
  headers: {
    Authorization: 'ACCESS_TOKEN',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: 'Margin scheme',
    recoverable: true,
    compound: false,
    taxExemptionCode: 'M13',
    taxExemptionReason: 'Regime da margem de lucro - Bens em segunda mão',
    _taxRates: [{
      rate: 0,
      dateValidFrom: '1970-01-01T00:00:00.000Z'
    }]
  })
});

// Option B: pass the PT 0% meaning directly on the document line
await fetch('https://api.spaceinvoices.com/v1/Organizations/{id}/documents', {
  method: 'POST',
  headers: {
    Authorization: 'ACCESS_TOKEN',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    type: 'invoice',
    _documentItems: [{
      name: 'Used bicycle',
      classification: 'product',
      quantity: 1,
      priceGross: 100,
      _documentItemTaxes: [{
        rate: 0,
        taxExemptionCode: 'M13',
        taxExemptionReason: 'Regime da margem de lucro - Bens em segunda mão'
      }]
    }]
  })
});  

Payment Types

Payment Type SAFT-PT Code
cash NU (Numerário)
bank TB (Transferência Bancária)
credit-card CC
debit-card CD
paypal OU (Others)
online OU (Others)
other OU (Others)
coupon CO (Gift card)

Documents must include a footer containing these smart codes:

  • [starting capital]
  • [company number]
  • [tax number]
  • [address]
  • [city]
  • [zip]
  • [country]

NOTE: A default footer with these codes is set automatically if not provided.

Validation Rules

Rule Details
Document date Must be today's date (unless manual or canceled)
Custom totals Only allowed for manual documents (_at.isManual: true)
Cancellation Requires _at.cancellationReason
Negative quantities Only allowed on credit notes
Negative prices Not allowed
Crypto currencies Not supported for Portuguese documents
Character encoding Organization and client text fields must use Latin characters only (Latin Extended-A)

SAFT-PT Export

The SAFT-PT XML export can be generated for a given date range and includes all non-draft documents (invoices, credit notes, advances, estimates). The export conforms to SAFT-PT version 1.04_01.

The XML is generated with windows-1252 encoding. Portuguese characters such as ã are supported.