Developers
A real invoicing API, certified for Portugal
Issue Portuguese invoices, sync customers and products, straight from your own storefront, back-office, or automation. It's a REST API over the same path Descodify's own app uses, not a bolt-on.
Certified invoicing, coming soon
Get early access - free tier includes unlimited invoicing.
Already have an account? Create a key in Settings → Developers.
Three calls to your first certified invoice
Create a key in Settings → Developers, then create a customer and issue an invoice against it. Send prices as decimal strings — unitPrice, like "500.00" — in the invoice’s currency, which currencyCode sets once and defaults to EUR. Responses return them in minor units: millionths for invoice lines, hundredths elsewhere. VAT rates are integer percent, and an Idempotency-Key header is required whenever you issue.
curl https://descodify.pt/api/v1/customers \
-H "Authorization: Bearer dsc_live_..." \
-H "Content-Type: application/json" \
-d '{"customerType":"business","name":"Acme Lda","country":"PT"}'
curl https://descodify.pt/api/v1/invoices \
-H "Authorization: Bearer dsc_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f2c6b6a-2f0e-4b0a-9c0a-000000000001" \
-d '{
"invoiceType": "invoice",
"customerId": "<id from above>",
"action": "issue",
"items": [
{ "description": "Consulting", "quantity": 1, "unitPrice": "500.00", "vatRate": 23, "itemType": "services" }
]
}'The response is the issued invoice: sequential number, ATCUD, QR code data, and at_comm_status: "pending" while AT communication runs in the background. Full walkthrough, field-by-field, in the quickstart guide.
What's in the API
Customers, products and invoices with their full lifecycle, plus your issuer identity and the fiscal export file. Every response mirrors the same data model the app itself uses, so nothing is a stripped-down view.
Customers
List, get, create, update, delete. Business or private customers, with EU VAT number validation.
Products
List, get, create, update, delete. Goods or services, with their own VAT tier and unit price.
Invoices
Create a draft, issue it, cancel it, record a payment, fetch the PDF, and create credit or debit notes. Issuing runs the same certified path as the app: series, ATCUD, digital signature, AT communication. Recording a payment issues the receipt (recibo) down that same path.
Business profile
Read-only. Your issuer identity and VAT regime, so your integration knows how to construct a valid invoice.
Fiscal export file (SAF-T)
The full standard audit file for a year or a narrower period, the same XML your accountant and the tax authority ask for. It carries your whole fiscal record for the period, so it has its own scope and its own hourly budget. You can also export one month and keep the file, which records that the month was exported and gives you back the exact bytes you submitted.
Auth, scopes, and rate limits
Org-scoped API keys
The account owner creates a key in Settings → Developers. Each key belongs to exactly one organization, no org identifier in the URL, and acts with that owner's permissions, never more. Send it as Authorization: Bearer dsc_live_.... The secret is shown once, at creation.
Scopes
Eight scopes: customers:read, customers:write, the matching pair for products and invoices, and the same pair for saft. A write scope always covers read on the same resource, so pick only what your key needs. You can change a key’s scopes later in Settings → Developers without rotating it: the key and its secret stay the same, so nothing has to be redeployed. Removing a scope takes effect at once.
The export has its own resource on purpose. One export call returns your whole fiscal record for the period, so invoices:write does not cover it. A key you hand to an integration that only creates invoices cannot pull your ledger. saft:write is narrower again: it is what produces a month's export and keeps the file, while reading scopes only read what is already there. A month can be exported as many times as you need, and every file is kept.
Rate limits and fair usage
Every call is rate-limited per key under a fair-usage policy. Go over it and you get a 429 with a Retry-After header telling you how long to back off. Issuing a certified document, an invoice or a receipt, additionally has a daily quota (drafts and reads don't count against it). Hit it and that call returns a daily_quota_exceeded response with a mailto to request a higher threshold. The fiscal export is capped at 12 calls an hour per key, because it assembles a whole period rather than reading a row.
Certified, not just connected
A lot of invoicing APIs built for other markets will happily hand you back a PDF that isn't a legally valid Portuguese invoice. Software that issues Portuguese invoices has to be AT-certified, assign sequential ATCUD codes, and report every issued document to the tax authority. The API is a thin layer over the exact same path Descodify's own app uses, not a workaround.
Every request, and everything it touches, stays on infrastructure hosted in the EU (Germany).
Reference and terms
The full OpenAPI 3.1 document is public, no key required, at /api/v1/openapi.json. Browse it interactively on the API reference page, or read the quickstart guide for a worked example of every step. Using the API is covered by the same Terms of Service as the rest of the product.
AI clients (MCP)
Descodify also speaks the Model Context Protocol. Point your AI assistant at @descodify/mcp, an open-source server, and it can issue certified invoices, create customers, and manage products in plain language, over the same certified path as the REST API and the app itself.
MCP is an open protocol, not ours. The same server runs unmodified in Claude Desktop, Claude Code, Gemini CLI, Cursor, Windsurf, VS Code's Copilot agent mode, Cline, and Zed. Use whichever AI client you already have.
Before you start
Create an org-scoped API key in Settings → Developers (see Auth, scopes, and rate limits above), with whichever of the customers, products, and invoices scopes your assistant needs.
Claude Desktop
Add to claude_desktop_config.json:
{
"mcpServers": {
"descodify": {
"command": "npx",
"args": ["-y", "@descodify/mcp"],
"env": { "DESCODIFY_API_KEY": "dsc_live_..." }
}
}
}Claude Code
One command from your terminal:
claude mcp add descodify \
--env DESCODIFY_API_KEY=dsc_live_... \
-- npx -y @descodify/mcpGemini CLI
Same block, in ~/.gemini/settings.json:
{
"mcpServers": {
"descodify": {
"command": "npx",
"args": ["-y", "@descodify/mcp"],
"env": { "DESCODIFY_API_KEY": "dsc_live_..." }
}
}
}Cursor
Same block, in .cursor/mcp.json:
{
"mcpServers": {
"descodify": {
"command": "npx",
"args": ["-y", "@descodify/mcp"],
"env": { "DESCODIFY_API_KEY": "dsc_live_..." }
}
}
}Every client above defaults to https://descodify.pt. Set DESCODIFY_BASE_URL if you're pointing at a self-hosted or dev instance.
Pair the server with the public descodify skill on skills.sh. It teaches an agent the safe flow: read your business profile before building an invoice, confirm the line items and totals with you before issuing, and correct a mistake with a credit note rather than an edit, whether it's calling through the MCP server or the raw API directly.
Issuing stays irreversible either way: a real sequential number, communicated to the AT, correctable only by credit note. The server generates a fresh idempotency key on every issue, so a dropped connection and a retry from your AI can't mint the same invoice twice.
Source is MIT-licensed and public at github.com/descodify/mcp. The full OpenAPI document above covers every call it wraps.
Create your account and generate your first API key from Settings → Developers.
Get started free