Skip to content

Portugal (ATCUD and SAF-T)

Portugal is a country-specific compliance layer. This guide describes the fields that Space Invoices asks for and the Portugal workflows it currently supports. It does not decide whether a transaction is eligible for a VAT exemption or replace advice from your accountant.

Start here

For a first integration, follow entity → ATCUD series → invoice → payment → credit note → exports. The sandbox launch walkthrough runs this flow with a fresh company and checks the expected totals. Use the sections below for individual field requirements and the current product boundaries.

Supported workflows

Entity responses include country_rules.capabilities, a read-only snapshot of country availability. Use it to decide which actions to offer, then apply your plan and user permissions. A plan upgrade, support account, or sandbox environment cannot enable an unsupported country operation. The API also checks availability when an action runs.

Portugal supports invoices and advance invoices (FT), credit notes (NC), and estimates (OR). Delivery notes and other fiscal document families are not available. Issued documents use the protected Portugal PDF layout and Portuguese language; drafts, editing issued documents, document email, and e-invoicing are unavailable in the current product.

Native iOS is unavailable for Portugal (native_ios: false). The iOS organization picker excludes Portuguese organizations in both live and sandbox environments, and the API rejects native iOS access to them. Portugal remains available through the supported web and API workflows described here.

Ordinary creation and manual recovery

Use the standard create endpoints for ordinary issuance. The /custom endpoints reject ordinary Portugal creation with a validation error (422). Manual recovery is a separate workflow: it requires pt.manual: true, an active manual series, and the original paper-document reference. Setting the manual flag alone is insufficient. Recovery does not enable importing invoices already issued by another invoicing system.

A new recovery series starts at 1. Provide the paper series in pt.manual_series_code and its original number in pt.manual_sequential_number. Use a digit string such as "00001" to preserve leading zeros; positive integer values remain supported. The same paper original cannot be recovered twice while its previous recovery is active, even through another recovery series.

Keep the paper document’s actual issue date and recover documents in date order within each series. Dates cannot be in the future or earlier than the last document in that recovery series. If you need to recover an older original after newer documents, use a separate recovery series registered for that purpose.

Direct payments on Portugal credit notes are unavailable, including inline payments during creation and updating or restoring previously recorded direct payments. Existing direct payments can still be deleted where the document’s state permits it. Applying a credit note to an invoice remains a separate settlement operation; ordinary invoice payments and recording payment on an advance invoice remain available.

Applying an advance invoice to a final invoice is currently unavailable (advance_invoice_application: false). The generic settlement flow only reduces the amount payable; Portugal also requires the final invoice to account for VAT already charged on the advance. Creating linked applications through the API is rejected until that tax treatment is supported.

For card payments, send type: "credit_card" or type: "debit_card"; SAF-T records these as CC and CD. The older card value remains accepted and exports as CC, so integrations should use an explicit method when the card is debit.

Cancelled Portuguese documents retain readable payment history but cannot receive payment changes. Payment reads with include=Invoice,CreditNote,AdvanceInvoice expose voided_at so clients can disable those controls. Other countries retain their own cancellation and settlement workflows.

Automation and integrations

To disable previously saved e-invoicing settings for Portugal, send either enabled: false or auto_send: false to the e-invoicing settings endpoint. Both settings are cleared together; explicitly enabling either remains unavailable.

Recurring invoice creation and generation are unavailable for Portugal until the recurring adapter supports all required fiscal fields. When a background run encounters an existing active Portugal schedule, it pauses the schedule, records last_error and last_error_at, and queues a recurring_invoice.generation_failed webhook for subscribed integrations. Historical schedules remain viewable and deletable; Edit and Resume are unavailable.

Order ingestion and standard invoice processing remain available, subject to normal fiscal validation. Unsupported document-email and automatic-reissue options default to off, and execution checks them again for older integrations. Keep invoice issuance requirements, such as active series and valid line taxes, configured before enabling automatic processing.

Set up the entity

Start with the normal entity identity: legal name, Portuguese NIF, and address. The SAF-T specification uses the commercial-registry office plus registration number for CompanyID; where there is no commercial registration, it uses the NIF. Telephone and email are useful contact details, but are optional in SAF-T.

The additional company details depend on the entity’s legal form. Set the form in settings.legal_details.legal_form:

Legal formWhat to provide
sole_traderNo company registration or starting-capital fields are required for this setup.
limited_liability_company, public_limited_company, or partnership_limited_by_sharesProvide settings.legal_details.registration_office, company_number, and starting_capital.
other_companyProvide the registration office and registration number; starting capital is optional.

Every Portugal entity must explicitly select settings.legal_details.legal_form. Company number and share capital alone do not replace that selection; provide the registry office separately for a company. paid_up_capital, equity, and in_liquidation are conditional disclosures: add them when they apply to the legal form and jurisdiction.

Registry name and number: one combined limit

For a company, the API combines settings.legal_details.registration_office, one space, and company_number. The result must fit 50 characters, matching SAF-T CompanyID. Both fields remain separate in your request; the separator counts towards the limit. This limit comes from the SAF-T structure, field 1.2.

For example, C.R.C. Lisboa plus a nine-digit company number uses 23 characters including the space. Conservatória do Registo Comercial de Lisboa plus that number uses 54, so it is too long. Use a recognizable abbreviation of the correct registry, and keep the registration number unchanged. Do not silently truncate either field. The issuer’s tax_number must always identify the company issuing the invoice, not the integration provider.

Create Portugal entitytypescript
const _entity = await sdk.entities.create({
  name: "João Silva",
  country: "Portugal",
  country_code: "PT",
  tax_number: "200000004",
  currency_code: "EUR",
  locale: "pt-PT",
  address: "Avenida da Liberdade 10",
  city: "Lisboa",
  post_code: "1000-001",
  settings: {
    legal_details: {
      legal_form: "sole_trader",
    },
  },
});

Create the ATCUD series

Create an active ATCUD series for each supported Portugal document family you issue. In production, a validation code has at least eight uppercase consonants or digits 2–9 (for example, BCDF2345). In sandbox it starts with AA, is uppercase alphanumeric, and has at least eight characters (for example, AABC2345). There is no arbitrary maximum length.

This is format validation when the series is created; it does not prove that the code is registered with AT. Issuing a document uses the saved series without repeating that format check. Invoices and advance invoices use the same FT series family.

Series codes are case-insensitive within a document family: SALES and sales cannot be registered separately. A series keeps its numbering across calendar years.

When you stop using a series permanently, retire it in Portugal settings or call POST /fiscalization/pt/atcud-series/{id}/retire. Retired series remain available in your history but cannot issue new documents or be reopened. This action closes the series in Space Invoices; it does not communicate its finalization to AT. Complete the required finalization with AT separately.

The example below uses a sandbox validation code. For production, substitute the actual code returned by AT; a plausible-looking code is not registration.

Create sandbox ATCUD seriestypescript
const _series = await sdk.ptAtcudSeries.create({
  document_type: "invoice",
  series_code: "FT2026A",
  validation_code: "AABC2345",
  first_number: "FT FT2026A/00001",
  start_date: "2026-01-01",
  is_manual: false,
  initial_sequence: 1,
});

Issue an ordinary invoice

Current Portugal invoices and advance invoices are full FT documents. A final-consumer recipient changes the customer identity used for export; it does not make the document an FS simplified invoice.

Use an exportable recipient, an active series, and clear line items. A fiscal item name is limited to 200 characters; keep the longer item description when it helps explain the supply. unit is optional and exports as UN when absent or blank. Each Portugal line currently supports one explicit tax row. Select the tax that applies to the line rather than combining multiple tax rows.

Documents can be in a foreign currency, while the Portugal reporting output uses EUR fiscal amounts. Keep the document currency and its exchange-rate information with the document.

date_service and date_service_to describe the supply separately from the issue date. For an issued service period, SAF-T uses its completion date; otherwise it uses an earlier service date when applicable. Ordinary issuance is restricted to today. The dedicated manual-document workflow is the exception; it is not a way to backdate an ordinary electronic invoice.

Dates and the Portuguese calendar day

For current ordinary issuance, “today” means the API’s Portugal fiscal day in Europe/Lisbon. Send date-only values such as 2026-09-10; do not derive them with toISOString().slice(0, 10), which uses UTC and can select the wrong day near midnight. This is the API’s current issuance policy; service and payment dates describe when those events actually happened.

Use date for issue date, date_service / date_service_to for supply or a service period, and payments[].date for payment date. Convert each event’s timestamp separately. The walkthrough uses one formatter with an explicit timezone. If a long-running flow crosses midnight, refresh the issue date before creating the next document. For an ambiguous retry, keep the original body and request ID; see idempotency.

Identifying the operator

pt.operator_id is optional, non-secret operational identification up to 30 characters. If you omit it, Space Invoices derives it from the authenticated principal. Do not provide a personal operator first name, last name, or NIF for this purpose. This value becomes SAF-T SourceID. Integrations using one API key for several staff members can send a stable staff identifier, such as reception-1, on each document. It does not need to differ from the entity tax number, but a tax number is not required or recommended for identifying staff.

There are no Portugal operator defaults to configure on the entity or user. The former personal operator_first_name, operator_last_name, operator_tax_number and account_* aliases are temporarily accepted and discarded on document requests; remove them from your integration. The former settings.pt entity object and PUT /users/me/settings/pt endpoint have been removed.

Create issued invoicetypescript
const _invoice = await sdk.invoices.create({
  customer: {
    name: "Porto Payloads Lda",
    country: "Portugal",
    country_code: "PT",
    tax_number: "516403125",
    address: "Rua de Santa Catarina 100",
    city: "Porto",
    post_code: "4000-442",
  },
  items: [
    {
      name: "Ground operations service",
      quantity: 1,
      price: 100,
      classification: "service",
      unit: "UN",
      taxes: [{ rate: 23, classification: "standard" }],
    },
    {
      name: "Exempt training service",
      quantity: 1,
      price: 50,
      classification: "service",
      unit: "UN",
      taxes: [
        {
          rate: 0,
          pt_exemption_code: "M07",
          pt_exemption_reason: "Isento de IVA ao abrigo do artigo 9.º do CIVA",
        },
      ],
    },
  ],
  pt: {
    series_id: "atc_000000000000000000000001",
  },
});

VAT exemptions

A zero-rate line needs a supported AT exemption code and a non-blank legal reason of at most 60 characters (the SAF-T limit). Choose the code for the actual transaction and preserve its legal meaning; the API validates length but does not decide exemption eligibility or truncate legal text. A reusable tax can contain pt_exemption_code and pt_exemption_reason; a document can override both in items[].taxes[].

{
"rate": 0,
"pt_exemption_code": "M07"
}

For standard exemptions, send pt_exemption_code and let the API fill the legal wording. For example, M07 resolves to Isento artigo 9.º do CIVA. You can also save the code on a tax and reference that tax from document lines.

pt_exemption_reason is an optional wording override, limited to 60 characters. Use it when the issuer needs a specific legal explanation. M19 (other exemptions) has no automatic wording: supply the applicable legal basis. The issuer remains responsible for choosing the correct exemption; a code does not establish eligibility.

For M19, replace the placeholder below with the legal provision that actually applies to the transaction:

{
"rate": 0,
"pt_exemption_code": "M19",
"pt_exemption_reason": "<applicable legal provision>"
}

Missing, empty or whitespace-only M19 wording blocks issuance with a validation error. The web tax form asks for it before saving. The API can save an incomplete reusable tax, but you must provide the reason on that tax or on the document line before issuing a document.

Space Invoices saves the effective wording on each issued line and uses it for both printing and SAF-T. Later tax edits or catalogue updates do not change issued wording. This also applies to credit notes, which retain the original tax treatment.

Standard wording and overrides

The API’s standard wording follows AT’s exemption-code table. The web tax form fills the same suggestions and lets you edit them.

CodeAutomatic wordingCharacters
M07Isento artigo 9.º do CIVA25
M15Regime da margem de lucro-Objetos de coleção e antiguidades59
M21IVA - não confere direito à dedução35
M35, M40, M42, M43IVA - autoliquidação20

Standard mentions use ASCII hyphens. M15 removes the spaces around the hyphen to fit the 60-character SAF-T field while retaining every word; this formatting is not a separately published AT abbreviation. M35, M40, M42 and M43 have the same mention but different legal grounds, so choose the appropriate code. M35 applies to qualifying construction or rehabilitation transactions under Decreto-Lei n.º 97/2026, for invoices issued after 1 July 2026, as specified in AT’s table; it does not replace M31 for other construction reverse-charge cases.

When supplied, your custom reason takes precedence over the automatic wording. An inline code without an explicit tax reference uses its own standard wording, even if the API matches a saved tax by rate internally. When you select a saved tax by tax_id or classification, its matching wording is inherited. When only the code changes, wording from a different saved-tax code is not inherited. Omitting or sending null/blank for the reason uses the explicitly selected saved tax or standard wording; it does not remove the required explanation from an exempt document. Replacing a saved tax with a new code and no reason resolves fresh wording for the new code.

AT FAQ 2781 requires both code and reason in SAF-T, with the reason reflecting the legal expression or applicable provision printed on the document. The API fills both even when you supply only a standard code.

Discounts

Discounts belong to each line’s discounts array. Use type: "percent" and value: 50 for 50%, not 0.5, and do not send a standalone discount field.

{
"name": "Eligible exempt service",
"classification": "service",
"quantity": 1,
"price": 40,
"discounts": [{ "type": "percent", "value": 50 }],
"taxes": [{
"rate": 0,
"pt_exemption_code": "M07"
}]
}

For a transaction eligible for M07, this line produces a €20.00 document total. For taxable lines, VAT also affects the final total. Use the API’s calculation result; the two supported calculation modes differ when several discounts are combined.

Credit notes

Every Portugal credit note must reference an issued original invoice from the same entity. Send linked_documents as an array of original invoice ID strings, for example [invoice.id]. The correction keeps the original recipient and currency. Multiple partial credit notes are supported, but their combined quantities and amounts cannot exceed the original line. Voiding a credit note releases its reserved balance.

Before preparing a correction, call GET /invoices/{id}/credit-options (JavaScript SDK: invoices.getCreditOptions(id, options)). It returns items with invoice_item_id and remaining_quantity, plus fully_credited. Copy the original line details and use only the remaining quantities. This read-only response does not reserve the balance; issuance checks it again, including concurrent corrections.

For ordinary partial credits, the API apportions the original net and VAT amounts across the credited quantities. Rounding cents are distributed between corrections so the final credit uses the exact remaining balance. Any difference inherited from an original gross price is included in the document’s rounding_correction, preserving the allocated net and VAT amounts. Use the calculation or document preview for the next correction’s amount; multiplying the unit price and tax rate again can differ by a cent. If you send expected_total_with_tax, use that previewed amount. Issuance rechecks the balance under a lock, so refresh the preview if another correction was issued meanwhile.

Create a correction from its original invoice rather than duplicating a previous credit note. The current API accepts regular invoice IDs (inv_) as originals; correcting an advance invoice (adv_) is not yet supported. Advance corrections are a separate required workflow, not a prohibition on correcting advance invoices.

Use pt.correction_reason for a short reason of up to 50 characters, or place a concise correction note on the document. Cancelling an invoice voids it in place; create a linked credit note when the business correction needs a credit note.

{
"linked_documents": ["inv_6595a27b5d35015c3ef0c3fd"],
"pt": {
"series_id": "pt_credit_note_series_00000000000001",
"correction_reason": "Returned goods"
}
}

Choose the reason for what actually happened. Examples that fit the 50-character limit include:

  • Billing mistake: Anulação por erro de faturação.
  • Returned goods: Devolução de mercadoria.
  • Refunded prepaid pack: Anulação de pack reembolsado.

A manual form can offer an editable suggestion; automated paths should send their own accurate reason. A credit note records the correction. It does not itself send money back to a card or bank account, and direct payments on Portuguese credit notes are not supported.

If you reduce VAT after a correction, keep evidence that the customer learned of the correction or was reimbursed. Use the existing document attachment workflow to retain that evidence with the document.

Export SAF-T PT

SAF-T PT is an audit export, not a replacement for normal document records. The default monthly export covers one selected calendar month. The optional audit mode covers the full selected year from 1 January to 31 December, or through today for the current year; it does not accept a document selection.

This product policy is deliberately stricter and more focused than the broader extraction cases described in the SAF-T specification. Select the period you need, then export the complete output for that mode.

Unit prices preserve the net price after discounts before line rounding, including when prices were entered with VAT. The export uses the document’s saved calculation mode and exchange rate. Line totals, VAT and the signed document total retain their issued values.

Both calculation modes are available for Portugal. b2b_standard adds percentage discounts against the original line subtotal; b2c_gross_discount applies discounts successively against the remaining VAT-inclusive amount. Choose the mode that matches your pricing agreement. For VAT-inclusive calculations, the API derives and rounds the net amount, then takes VAT as the difference from the fixed gross amount. For example, €10.03 including 23% VAT gives €8.15 net and €1.88 VAT. Calculation previews and issuance use the same rule.

Export SAF-Ttypescript
const _xml = await sdk.ptSafT.generateReport({
  from: "2026-01-01",
  to: "2026-01-31",
});

const _auditXml = await sdk.ptSafT.generateReport({
  mode: "audit",
  from: "2026-01-01",
});

SAF-T and PDF archives use different export flows

SAF-T XMLPDF archive
StartGET /fiscalization/pt/saft/reportPOST /documents/export/pdf
Period fieldsQuery: from, toJSON: date_from, date_to
Document selectionComplete output for the selected export modetypes: ["invoice", "credit_note"] for these two families; add other supported families when needed
ResultXML file in the responseJob ID; poll GET /documents/export/pdf/{id}
DownloadSave the original response bytesWait for status: "completed", then use file_url

Do not stop polling because progress reaches a particular value; use status. Surface failed jobs and their error. A completed job with no file_url is not downloadable. If polling times out, retain the job ID and resume polling that job rather than immediately starting another export.

SAF-T is Windows-1252 XML. Save its binary response without decoding and re-encoding it as UTF-8. The JavaScript SDK returns a Blob for this export. PDF archives are ZIP files, not XML. Download URLs may be signed storage URLs or authenticated API URLs; send your API key only to your configured API origin, never to an external storage host.

Sandbox launch walkthrough

Use a sandbox account API key that can create entities. The example creates a fresh company each time; do not run it with a live key or copy its example NIF into a customer’s production configuration. M07 here models a service whose exemption eligibility has already been established. It is not a default for every clinic or service.

Install @spaceinvoices/js-sdk, save the example as portugal-launch.ts, set SI_SANDBOX_KEY in your server environment, and run it with your TypeScript runner. It writes portugal-launch.xml and portugal-launch.zip into the working directory.

The complete Node.js/TypeScript example creates two invoices (€40 and €20), a €40 debit-card payment on the first invoice, a linked €40 credit note, and both exports. A new FT series gives FT DEMO/00001 and FT DEMO/00002; the NC series gives NC DEMO/00001. The payment remains a historical fact when the invoice is credited; process any actual refund separately.

Run the sandbox launch flowtypescript
import SpaceInvoices from "@spaceinvoices/js-sdk/sdk";
import * as fs from "node:fs";

const key = process.env.SI_SANDBOX_KEY;
if (!key?.startsWith("sk_sandbox_")) throw new Error("Set a sandbox account API key in SI_SANDBOX_KEY");
const sdk = new SpaceInvoices(key);

function portugalDate(instant = new Date()) {
  const parts = new Intl.DateTimeFormat("en", {
    timeZone: "Europe/Lisbon", year: "numeric", month: "2-digit", day: "2-digit",
  }).formatToParts(instant);
  const part = (type: string) => parts.find((value) => value.type === type)!.value;
  return `${part("year")}-${part("month")}-${part("day")}`;
}
const today = portugalDate();

// Example identity for this sandbox walkthrough only.
const entity = await sdk.entities.create({
  name: "Clinica Demo, Lda.", country: "Portugal", country_code: "PT",
  tax_number: "501964843", company_number: "501964843", starting_capital: 5000,
  address: "Rua de Teste 10", city: "Lisboa", post_code: "1000-001",
  currency_code: "EUR", locale: "pt-PT",
  settings: { legal_details: {
    legal_form: "limited_liability_company", registration_office: "C.R.C. Lisboa",
  } },
});
if (entity.environment !== "sandbox") throw new Error("This walkthrough requires a sandbox entity");
const context = { entity_id: entity.id };
console.log("Save this entity ID:", entity.id);

const ft = await sdk.ptAtcudSeries.create({
  document_type: "invoice", series_code: "DEMO", validation_code: "AABC2345",
  first_number: "FT DEMO/00001", start_date: today, initial_sequence: 1,
}, context);
const nc = await sdk.ptAtcudSeries.create({
  document_type: "credit_note", series_code: "DEMO", validation_code: "AABC2346",
  first_number: "NC DEMO/00001", start_date: today, initial_sequence: 1,
}, context);

const customer = { name: "Consumidor final", is_end_consumer: true, country: "Portugal", country_code: "PT" };
// Use M07 only after establishing that the actual supply qualifies.
const line = {
  name: "Eligible exempt service", classification: "service" as const,
  quantity: 1, unit: "UN", price: 40,
  taxes: [{ rate: 0,
    pt_exemption_code: "M07" }],
};
const first = await sdk.invoices.create({
  date: portugalDate(), date_service: today, customer, items: [line],
  pt: { series_id: ft.id, operator_id: "reception-1" },
  payments: [{ type: "debit_card", amount: 40, date: today }],
}, { ...context, request_id: `${entity.id}:launch:invoice-1` });
const discounted = await sdk.invoices.create({
  date: portugalDate(), date_service: today, customer,
  items: [{ ...line, discounts: [{ type: "percent", value: 50 }] }],
  pt: { series_id: ft.id, operator_id: "reception-1" },
}, { ...context, request_id: `${entity.id}:launch:invoice-2` });
const credit = await sdk.creditNotes.create({
  date: portugalDate(), customer, linked_documents: [first.id], items: [line],
  pt: { series_id: nc.id, operator_id: "reception-1", correction_reason: "Anulação por erro de faturação" },
}, { ...context, request_id: `${entity.id}:launch:credit-1` });
if (first.total_with_tax !== 40 || discounted.total_with_tax !== 20 || credit.total_with_tax !== 40) {
  throw new Error("Unexpected document totals; inspect the responses before continuing");
}
console.log(first.number, discounted.number, credit.number);

// This run's dates must be in one month for a single monthly SAF-T export.
const through = portugalDate();
if (today.slice(0, 7) !== through.slice(0, 7)) {
  throw new Error("The run crossed a month boundary; export the two months separately");
}
const from = `${today.slice(0, 7)}-01`;
const xml = await sdk.ptSafT.generateReport({ from, to: through }, context);
fs.writeFileSync("portugal-launch.xml", new Uint8Array(await xml.arrayBuffer()));

const job = await sdk.exports.startPdfExport({
  types: ["invoice", "credit_note"], date_from: from, date_to: through,
}, context);
console.log("Save this export job ID:", job.id);
let downloadUrl: string | null = null;
for (let attempt = 0; attempt < 60; attempt++) {
  const status = await sdk.exports.getPdfStatus(job.id, context);
  if (status.status === "failed") throw new Error(status.error ?? "PDF export failed");
  if (status.status === "completed") {
    if (!status.file_url) throw new Error("Archive unavailable; inspect the export job");
    downloadUrl = status.file_url;
    break;
  }
  await new Promise((resolve) => setTimeout(resolve, 2000));
}
if (!downloadUrl) throw new Error(`Export still running. Resume polling job ${job.id}; do not start another.`);

// Signed storage URLs need no API credential; API download routes do.
const apiOrigin = "https://eu.spaceinvoices.com";
const url = new URL(downloadUrl, apiOrigin);
const headers = url.origin === apiOrigin
  ? { Authorization: `Bearer ${key}`, "x-entity-id": entity.id }
  : undefined;
const archive = await fetch(url, { headers });
if (!archive.ok) throw new Error(`Archive download failed: ${archive.status}`);
fs.writeFileSync("portugal-launch.zip", new Uint8Array(await archive.arrayBuffer()));

Inspect the saved PDFs for issuer details, ATCUD, QR, exemption wording and the credit-note reference. Check that SAF-T includes all three document numbers, the intended operator ID, payment method and totals. A schema-valid file is one check; it does not establish exemption eligibility or confirm production series registration.

Before production, repeat the setup with the issuer’s real legal details and the validation codes returned by AT. Sandbox codes and document history do not become production series. Keep entity/series/document IDs in your integration, and use stable business-operation request IDs for document retries. Do not rerun the entire onboarding flow to recover from a timeout.

Current boundaries

Portuguese businesses cannot be imported through legacy migration, in either live or sandbox environments.

The following Portugal workflows are not currently implemented: cash-accounting VAT, self-billing, third-party invoicing, FS simplified invoices, and delivery-note creation. Do not represent an ordinary FT document as one of these documents.

Sources

See the PT ATCUD Series API Reference, PT SAF-T API Reference, and Invoices API Reference.