Portugal Quickstart
This quickstart covers the essential steps to get started with the SIGN PT API, from authentication to creating your first fiscal record. For a more detailed walkthrough, refer to our integration guide.
Prerequisites
Section titled “Prerequisites”- A fiskaly account (sign up at hub.fiskaly.com)
- An API Key and Secret for an Organization
GROUPin the TEST environment - AT Portal das Finanças subuser credentials for the taxpayer (real credentials required in LIVE only)
Your API Secret is shown only once. Store it immediately in a secure location.
Authenticate
curl -X POST https://test.api.fiskaly.com/tokens \ -H "Content-Type: application/json" \ -H "X-Api-Version: 2026-05-04" \ -d '{ "content": { "type": "API_KEY", "key": "YOUR_API_KEY", "secret": "YOUR_API_SECRET" } }'const BASE = "https://test.api.fiskaly.com"; const API_VERSION = "2026-05-04"; const authResp = await fetch(`${BASE}/tokens`, { method: "POST", headers: { "Content-Type": "application/json", "X-Api-Version": API_VERSION, }, body: JSON.stringify({ content: { type: "API_KEY", key: "YOUR_API_KEY", secret: "YOUR_API_SECRET" }, }), }); const { access_token } = await authResp.json(); const headers = { "Authorization": `Bearer ${access_token}`, "Content-Type": "application/json", "X-Api-Version": API_VERSION, };import requests, uuid BASE = "https://test.api.fiskaly.com" API_VERSION = "2026-05-04" auth = requests.post(f"{BASE}/tokens", json={ "content": {"type": "API_KEY", "key": "YOUR_API_KEY", "secret": "YOUR_API_SECRET"} }, headers={"X-Api-Version": API_VERSION}) access_token = auth.json()["access_token"] hdrs = {"Authorization": f"Bearer {access_token}", "X-Api-Version": API_VERSION}// POST https://test.api.fiskaly.com/tokens // Headers: Content-Type: application/json, X-Api-Version: 2026-05-04 // Body: {"content":{"type":"API_KEY","key":"...","secret":"..."}}using var client = new HttpClient(); client.DefaultRequestHeaders.Add("X-Api-Version", "2026-05-04"); var authResp = await client.PostAsJsonAsync( "https://test.api.fiskaly.com/tokens", new { content = new { type = "API_KEY", key = "YOUR_API_KEY", secret = "YOUR_API_SECRET" } });📘NoteSIGN PT uses the same unified API platform as SIGN IT and SIGN FR. All requests require the
X-Api-Versionheader. Write operations also need anX-Idempotency-Keyheader with a UUIDv3 or a UUIDv4 value.Create an Organization
UNITcurl -X POST https://test.api.fiskaly.com/organizations \ -H "Authorization: Bearer ${ACCESS_TOKEN}" \ -H "Content-Type: application/json" \ -H "X-Api-Version: 2026-05-04" \ -H "X-Idempotency-Key: $(uuidgen)" \ -d '{ "content": { "type": "UNIT", "name": "My Portuguese Merchant" } }'const org = await fetch(`${BASE}/organizations`, { method: "POST", headers: { ...headers, "X-Idempotency-Key": crypto.randomUUID() }, body: JSON.stringify({ content: { type: "UNIT", name: "My Portuguese Merchant" }, }), }).then(r => r.json()); const orgId = org.id;org = requests.post(f"{BASE}/organizations", headers={ **hdrs, "X-Idempotency-Key": str(uuid.uuid4()) }, json={"content": {"type": "UNIT", "name": "My Portuguese Merchant"}}).json() org_id = org["id"]// POST /organizations // Headers: X-Idempotency-Key: <uuid> // Body: {"content":{"type":"UNIT","name":"My Portuguese Merchant"}}client.DefaultRequestHeaders.Add("X-Idempotency-Key", Guid.NewGuid().ToString()); var org = await client.PostAsJsonAsync($"{BASE}/organizations", new { content = new { type = "UNIT", name = "My Portuguese Merchant" } });Create a Subject API Key and authenticate
Create an API key for the
UNIT, then authenticate with it:# Create Subject API Key (scoped to the `UNIT`) curl -X POST https://test.api.fiskaly.com/subjects \ -H "Authorization: Bearer ${ACCESS_TOKEN}" \ -H "Content-Type: application/json" \ -H "X-Api-Version: 2026-05-04" \ -H "X-Idempotency-Key: $(uuidgen)" \ -H "X-Scope-Identifier: ${ORG_ID}" \ -d '{"content": {"type": "API_KEY"}}' # Authenticate with the new API Key curl -X POST https://test.api.fiskaly.com/tokens \ -H "Content-Type: application/json" \ -H "X-Api-Version: 2026-05-04" \ -d '{"content": {"type": "API_KEY", "key": "NEW_KEY", "secret": "NEW_SECRET"}}'// Create Subject API Key scoped to the `UNIT` const subject = await fetch(`${BASE}/subjects`, { method: "POST", headers: { ...headers, "X-Idempotency-Key": crypto.randomUUID(), "X-Scope-Identifier": orgId, }, body: JSON.stringify({ content: { type: "API_KEY" } }), }).then(r => r.json()); // Re-authenticate with the new key const newAuth = await fetch(`${BASE}/tokens`, { method: "POST", headers: { "Content-Type": "application/json", "X-Api-Version": API_VERSION, }, body: JSON.stringify({ content: { type: "API_KEY", key: subject.key, secret: subject.secret }, }), }).then(r => r.json()); headers.Authorization = `Bearer ${newAuth.access_token}`;# Create Subject API Key subject = requests.post(f"{BASE}/subjects", headers={ **hdrs, "X-Idempotency-Key": str(uuid.uuid4()), "X-Scope-Identifier": org_id, }, json={"content": {"type": "API_KEY"}}).json() # Re-authenticate new_auth = requests.post(f"{BASE}/tokens", headers={"X-Api-Version": API_VERSION}, json={"content": {"type": "API_KEY", "key": subject["key"], "secret": subject["secret"]}}).json() hdrs["Authorization"] = f"Bearer {new_auth['access_token']}"// POST /subjects with X-Scope-Identifier: <orgId> // Body: {"content":{"type":"API_KEY"}} // Then POST /tokens with the new key and secret// Create Subject API Key, then re-authenticate with the new credentials // POST /subjects → returns key + secret // POST /tokens → returns new access_tokenCreate Taxpayer, Location, and System
⚠️AT Portal das Finanças subuser requiredSIGN PT calls AT web services on the taxpayer’s behalf. Before creating a Taxpayer in LIVE, the merchant must create a dedicated Web Service subuser in the Portal das Finanças with permissions WSE, WFA, and WDT (where applicable) and accept the AT terms and conditions. In TEST, placeholder credentials are accepted.
# Create Taxpayer curl -X POST https://test.api.fiskaly.com/taxpayers \ -H "Authorization: Bearer ${ACCESS_TOKEN}" \ -H "Content-Type: application/json" \ -H "X-Api-Version: 2026-05-04" \ -H "X-Idempotency-Key: $(uuidgen)" \ -d '{ "content": { "type": "COMPANY", "name": { "legal": "Minha Empresa Lda." }, "address": { "line": { "type": "STREET_NUMBER", "street": "Rua Augusta", "number": "28" }, "code": "1100-053", "city": "Lisboa", "country": "PT" }, "fiscalization": { "type": "PT", "tax_id_number": "512345678", "email": "fiscal@minhaempresa.pt", "registration": { "capital": "50000.00", "office": "CRC Lisboa", "other": "sob NIF 512345678" }, "credentials": { "type": "AT", "username": "512345678/3", "password": "YOUR_AT_PASSWORD" } } } }' # Commission Taxpayer curl -X PATCH "https://test.api.fiskaly.com/taxpayers/${TAXPAYER_ID}" \ -H "Authorization: Bearer ${ACCESS_TOKEN}" \ -H "Content-Type: application/json" \ -H "X-Api-Version: 2026-05-04" \ -H "X-Idempotency-Key: $(uuidgen)" \ -d '{"content": {"state": "COMMISSIONED"}}'// Create Taxpayer const taxpayer = await fetch(`${BASE}/taxpayers`, { method: "POST", headers: { ...headers, "X-Idempotency-Key": crypto.randomUUID() }, body: JSON.stringify({ content: { type: "COMPANY", name: { legal: "Minha Empresa Lda." }, address: { line: { type: "STREET_NUMBER", street: "Rua Augusta", number: "28" }, code: "1100-053", city: "Lisboa", country: "PT", }, fiscalization: { type: "PT", tax_id_number: "512345678", email: "fiscal@minhaempresa.pt", registration: { capital: "50000.00", office: "CRC Lisboa", other: "sob NIF 512345678", }, credentials: { type: "AT", username: "512345678/3", password: "YOUR_AT_PASSWORD", }, }, }, }), }).then(r => r.json()); // Commission Taxpayer await fetch(`${BASE}/taxpayers/${taxpayer.id}`, { method: "PATCH", headers: { ...headers, "X-Idempotency-Key": crypto.randomUUID() }, body: JSON.stringify({ content: { state: "COMMISSIONED" } }), }); // Create + Commission Location and System follow the same pattern# Create Taxpayer taxpayer = requests.post(f"{BASE}/taxpayers", headers={ **hdrs, "X-Idempotency-Key": str(uuid.uuid4()) }, json={"content": { "type": "COMPANY", "name": {"legal": "Minha Empresa Lda."}, "address": { "line": {"type": "STREET_NUMBER", "street": "Rua Augusta", "number": "28"}, "code": "1100-053", "city": "Lisboa", "country": "PT", }, "fiscalization": { "type": "PT", "tax_id_number": "512345678", "email": "fiscal@minhaempresa.pt", "registration": { "capital": "50000.00", "office": "CRC Lisboa", "other": "sob NIF 512345678", }, "credentials": { "type": "AT", "username": "512345678/3", "password": "YOUR_AT_PASSWORD", }, }, }}).json() # Commission Taxpayer requests.patch(f"{BASE}/taxpayers/{taxpayer['id']}", headers={ **hdrs, "X-Idempotency-Key": str(uuid.uuid4()) }, json={"content": {"state": "COMMISSIONED"}})// POST /taxpayers // Body: {"content":{"type":"COMPANY","name":{"legal":"Minha Empresa Lda."}, // "address":{"line":{"type":"STREET_NUMBER","street":"Rua Augusta","number":"28"}, // "code":"1100-053","city":"Lisboa","country":"PT"}, // "fiscalization":{"type":"PT","tax_id_number":"512345678", // "email":"fiscal@minhaempresa.pt", // "registration":{"capital":"50000.00","office":"CRC Lisboa","other":"sob NIF 512345678"}, // "credentials":{"type":"AT","username":"512345678/3","password":"YOUR_AT_PASSWORD"}}}} // PATCH /taxpayers/{id} → {"content":{"state":"COMMISSIONED"}} // POST /locations → create BRANCH, then PATCH /locations/{id} → commission // POST /systems → create FISCAL_DEVICE, then PATCH /systems/{id} → commission// POST /taxpayers → create COMPANY with PT fiscalization // fiscalization.type = "PT", tax_id_number = NIF (9 digits) // fiscalization.email = fiscal contact email // fiscalization.registration = { capital, office (CRC), other } // fiscalization.credentials = { type: "AT", username: "<NIF>/<subuser-id>", password } // PATCH /taxpayers/{id} → {"content":{"state":"COMMISSIONED"}} // Same pattern for locations and systems💡Create Taxpayer firstAfter commissioning the Taxpayer, create one or several Locations (
type: "BRANCH") and one or several Systems (type: "FISCAL_DEVICE"). Update each toCOMMISSIONEDbefore creating Records. See the full integration guide for the complete sequence.⚠️Billing warningBilling begins as soon as a System is commissioned in the LIVE environment — at which point it will also start transmitting official documents to AT.
No billing or real transmissions take place in the TEST environment.
Create your first Record
Records require two calls: an INTENTION followed by a TRANSACTION.
# Part A: Intention curl -X POST https://test.api.fiskaly.com/records \ -H "Authorization: Bearer ${ACCESS_TOKEN}" \ -H "Content-Type: application/json" \ -H "X-Api-Version: 2026-05-04" \ -H "X-Idempotency-Key: $(uuidgen)" \ -d '{ "content": { "type": "INTENTION", "system": {"id": "YOUR_SYSTEM_ID"}, "operation": {"type": "TRANSACTION"} } }' # Part B: Transaction curl -X POST https://test.api.fiskaly.com/records \ -H "Authorization: Bearer ${ACCESS_TOKEN}" \ -H "Content-Type: application/json" \ -H "X-Api-Version: 2026-05-04" \ -H "X-Idempotency-Key: $(uuidgen)" \ -d '{ "content": { "type": "TRANSACTION", "record": {"id": "YOUR_INTENTION_ID"}, "operation": { "type": "RECEIPT", "document": { "number": "FS-2026/00001", "series": "FS-2026", "total_vat": { "amount": "2.30", "exclusive": "10.00", "inclusive": "12.30" } }, "entries": [ { "type": "SALE", "data": { "type": "ITEM", "text": "Product A", "unit": {"quantity": "1.00", "price": "10.00"}, "value": {"base": "10.00"}, "vat": { "type": "VAT_RATE", "code": "STANDARD", "percentage": "23.00", "amount": "2.30", "exclusive": "10.00", "inclusive": "12.30" } }, "details": {"concept": "GOOD"} } ], "payments": [ { "type": "CASH", "details": {"amount": "12.30", "currency": "EUR"} } ] } } }'// Part A: Intention const intention = await fetch(`${BASE}/records`, { method: "POST", headers: { ...headers, "X-Idempotency-Key": crypto.randomUUID() }, body: JSON.stringify({ content: { type: "INTENTION", system: { id: systemId }, operation: { type: "TRANSACTION" }, }, }), }).then(r => r.json()); // Part B: Transaction const record = await fetch(`${BASE}/records`, { method: "POST", headers: { ...headers, "X-Idempotency-Key": crypto.randomUUID() }, body: JSON.stringify({ content: { type: "TRANSACTION", record: { id: intention.id }, operation: { type: "RECEIPT", document: { number: "FS-2026/00001", series: "FS-2026", total_vat: { amount: "2.30", exclusive: "10.00", inclusive: "12.30" }, }, entries: [{ type: "SALE", data: { type: "ITEM", text: "Product A", unit: { quantity: "1.00", price: "10.00" }, value: { base: "10.00" }, vat: { type: "VAT_RATE", code: "STANDARD", percentage: "23.00", amount: "2.30", exclusive: "10.00", inclusive: "12.30", }, }, details: { concept: "GOOD" }, }], payments: [{ type: "CASH", details: { amount: "12.30", currency: "EUR" } }], }, }, }), }).then(r => r.json()); console.log("ATCUD:", record.compliance?.atcud); console.log("QR code:", record.compliance?.qr_code);# Part A: Intention intention = requests.post(f"{BASE}/records", headers={ **hdrs, "X-Idempotency-Key": str(uuid.uuid4()) }, json={"content": { "type": "INTENTION", "system": {"id": system_id}, "operation": {"type": "TRANSACTION"}, }}).json() # Part B: Transaction record = requests.post(f"{BASE}/records", headers={ **hdrs, "X-Idempotency-Key": str(uuid.uuid4()) }, json={"content": { "type": "TRANSACTION", "record": {"id": intention["id"]}, "operation": { "type": "RECEIPT", "document": { "number": "FS-2026/00001", "series": "FS-2026", "total_vat": {"amount": "2.30", "exclusive": "10.00", "inclusive": "12.30"}, }, "entries": [{ "type": "SALE", "data": { "type": "ITEM", "text": "Product A", "unit": {"quantity": "1.00", "price": "10.00"}, "value": {"base": "10.00"}, "vat": { "type": "VAT_RATE", "code": "STANDARD", "percentage": "23.00", "amount": "2.30", "exclusive": "10.00", "inclusive": "12.30", }, }, "details": {"concept": "GOOD"}, }], "payments": [{"type": "CASH", "details": {"amount": "12.30", "currency": "EUR"}}], }, }}).json() print("ATCUD:", record.get("compliance", {}).get("atcud"))// POST /records → INTENTION: system.id, operation.type = "TRANSACTION" // POST /records → TRANSACTION: // record.id = intention ID // operation.type = "RECEIPT" // document: number "FS-2026/00001", series "FS-2026", // total_vat { amount, exclusive, inclusive } // entries[0]: type "SALE", data { type "ITEM", text, unit, value, vat { type "VAT_RATE", code "STANDARD", ... } }, // details { concept "GOOD" } // payments[0]: type "CASH", details { amount, currency "EUR" } // Response includes compliance data: ATCUD, QR code content, hash excerpt, certificate number// POST /records → INTENTION, then POST /records → TRANSACTION // Each requires X-Idempotency-Key header // TRANSACTION body: record.id (intention ID), operation.type "RECEIPT", // document { number, series, total_vat { amount, exclusive, inclusive } }, // entries[0] { type "SALE", data { type "ITEM", text, unit, value, vat { type "VAT_RATE", ... } }, details { concept "GOOD" } }, // payments[0] { type "CASH", details { amount, currency "EUR" } } // Response contains compliance data: ATCUD, QR code, hash excerptOnce the Record reaches
COMPLETEDstate, SIGN PT returns the compliance payload for printing: ATCUD code, QR code content, hash excerpt, and the software certificate number.
Next Steps
Section titled “Next Steps”Was this page helpful?