Complete API reference for the NFE.io Node.js SDK v3.
- Installation
- Client
- Resources
- Service Invoices
- Companies
- Legal People
- Natural People
- Webhooks
- Transportation Invoices (CT-e)
- Inbound Product Invoices (NF-e Distribuição)
- Product Invoice Query (Consulta NF-e)
- Consumer Invoice Query (Consulta CFe-SAT)
- Legal Entity Lookup (Consulta CNPJ)
- Natural Person Lookup (Consulta CPF)
- Tax Calculation (Cálculo de Impostos)
- Tax Codes (Códigos Auxiliares)
- Product Invoices (NF-e Emissão)
- State Taxes (Inscrições Estaduais)
- Types
- Error Handling
- Advanced Usage
npm install nfe-ionew NfeClient(config: NfeConfig): NfeClientCreates a new NFE.io API client instance.
Parameters:
config- Client configuration object
Configuration Options:
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
apiKey |
string |
Yes* | process.env.NFE_API_KEY |
NFE.io API key |
environment |
'production' | 'development' |
No | 'production' |
API environment (both use same endpoint: https://api.nfe.io/v1) |
baseUrl |
string |
No | Auto-detected | Custom API base URL |
timeout |
number |
No | 30000 |
Request timeout in ms |
retryConfig |
RetryConfig |
No | See below | Retry configuration |
* API key is required but can be provided via NFE_API_KEY environment variable.
Retry Configuration:
| Property | Type | Default | Description |
|---|---|---|---|
maxRetries |
number |
3 |
Maximum retry attempts |
baseDelay |
number |
1000 |
Initial delay in ms |
maxDelay |
number |
30000 |
Maximum delay in ms |
retryableStatuses |
number[] |
[408, 429, 500, 502, 503] |
HTTP status codes to retry |
Examples:
// Basic usage
const nfe = new NfeClient({
apiKey: 'your-api-key',
environment: 'production'
});
// With environment variable
// Set NFE_API_KEY=your-api-key
const nfe = new NfeClient({
environment: 'production'
});
// Custom configuration
const nfe = new NfeClient({
apiKey: 'your-api-key',
timeout: 60000,
retryConfig: {
maxRetries: 5,
baseDelay: 2000,
maxDelay: 60000
}
});Update client configuration dynamically.
nfe.updateConfig({
environment: 'development',
timeout: 60000
});Set request timeout in milliseconds. (v2 compatibility)
nfe.setTimeout(60000); // 60 secondsSet or update API key. (v2 compatibility)
nfe.setApiKey('new-api-key');Get current client configuration.
const config = nfe.getConfig();
console.log('Environment:', config.environment);Poll a resource URL until it completes processing.
Parameters:
locationUrl- URL or path to polloptions- Polling configurationmaxAttempts(default:30) - Maximum polling attemptsintervalMs(default:2000) - Delay between attempts in ms
Returns: Promise resolving to the completed resource
Example:
const result = await nfe.serviceInvoices.create(companyId, data);
if (result.status === 'pending') {
const invoice = await nfe.pollUntilComplete(result.location, {
maxAttempts: 60,
intervalMs: 3000
});
}Check API connectivity and authentication.
const health = await nfe.healthCheck();
if (health.status === 'ok') {
console.log('API connection successful');
} else {
console.error('API error:', health.details);
}Get client diagnostic information.
const info = nfe.getClientInfo();
console.log('SDK Version:', info.version);
console.log('Node Version:', info.nodeVersion);
console.log('Environment:', info.environment);All resources follow a consistent pattern with standard CRUD operations plus resource-specific methods.
Resource: nfe.serviceInvoices
Complete service invoice (NFS-e) operations including creation, retrieval, email delivery, document downloads, and cancellation. Supports both synchronous and asynchronous invoice processing with automatic polling capabilities.
Create a new service invoice. Returns either a completed invoice (201) or async processing result (202).
Parameters:
| Parameter | Type | Description |
|---|---|---|
companyId |
string |
Company ID issuing the invoice |
data |
ServiceInvoiceData |
Invoice data (see structure below) |
Returns: Promise<ServiceInvoiceCreateResult> - Discriminated union:
- 201 Created (Synchronous): Returns complete
ServiceInvoicewithid,number,status - 202 Accepted (Asynchronous): Returns
{ flowStatus, flowMessage?, location? }for polling
Invoice Data Structure:
interface ServiceInvoiceData {
// Required fields
borrower: {
federalTaxNumber: number; // CPF (11 digits) or CNPJ (14 digits)
name: string;
email?: string;
address?: {
country: string; // 'BRA'
postalCode: string; // CEP: '01310-100'
street: string;
number: string;
additionalInformation?: string;
district: string;
city: {
code: string; // IBGE code: '3550308'
name: string;
};
state: string; // UF: 'SP'
};
};
cityServiceCode: string; // Municipal service code
description: string; // Service description
servicesAmount: number; // Amount in BRL (e.g., 1500.00)
// Optional fields
rpsSerialNumber?: string; // RPS series
rpsNumber?: number; // RPS number
issuedOn?: string; // ISO date: '2024-01-15'
deductions?: number; // Deductions amount
discountUnconditioned?: number; // Unconditional discount
discountConditioned?: number; // Conditional discount
taxes?: {
retainIss?: boolean;
iss?: number;
pis?: number;
cofins?: number;
inss?: number;
ir?: number;
csll?: number;
};
}Examples:
// Example 1: Basic invoice creation (may be sync or async)
const result = await nfe.serviceInvoices.create('company-id', {
borrower: {
federalTaxNumber: 12345678901,
name: 'João da Silva',
email: '[email protected]',
},
cityServiceCode: '10677',
description: 'Consulting services',
servicesAmount: 1500.00,
});
// Check if synchronous (201) or asynchronous (202)
if ('id' in result) {
// Synchronous - invoice issued immediately
console.log('Invoice issued:', result.number);
console.log('Status:', result.status);
} else {
// Asynchronous - needs polling
console.log('Processing:', result.flowStatus);
console.log('Poll URL:', result.location);
// Use pollUntilComplete or createAndWait instead
const invoice = await nfe.pollUntilComplete(result.location, {
intervalMs: 2000,
timeoutMs: 60000,
});
console.log('Invoice issued:', invoice.number);
}
// Example 2: Invoice with full details
const invoice = await nfe.serviceInvoices.create('company-id', {
borrower: {
federalTaxNumber: 12345678000190,
name: 'Acme Corporation',
email: '[email protected]',
address: {
country: 'BRA',
postalCode: '01310-100',
street: 'Av. Paulista',
number: '1578',
district: 'Bela Vista',
city: {
code: '3550308',
name: 'São Paulo',
},
state: 'SP',
},
},
cityServiceCode: '01234',
description: 'Software development services',
servicesAmount: 5000.00,
rpsSerialNumber: 'A',
rpsNumber: 123,
deductions: 100.00,
taxes: {
retainIss: false,
iss: 5.0, // 5%
},
});Error Handling:
ValidationError(400): Invalid invoice dataAuthenticationError(401): Invalid API keyNotFoundError(404): Company not foundInternalError(500): Server error
try {
const result = await nfe.serviceInvoices.create(companyId, data);
} catch (error) {
if (error instanceof ValidationError) {
console.error('Invalid data:', error.message);
} else if (error instanceof AuthenticationError) {
console.error('Invalid API key');
}
}createAndWait(companyId: string, data: ServiceInvoiceData, options?: WaitOptions): Promise<ServiceInvoice>
⭐ Recommended: Create invoice with automatic polling for async processing. Simplifies async workflow.
Parameters:
| Parameter | Type | Description |
|---|---|---|
companyId |
string |
Company ID |
data |
ServiceInvoiceData |
Invoice data |
options |
WaitOptions |
Polling configuration (optional) |
Wait Options:
| Option | Type | Default | Description |
|---|---|---|---|
pollingInterval |
number |
2000 |
Delay between status checks (ms) |
maxWaitTime |
number |
60000 |
Maximum wait time (ms) |
Returns: Promise<ServiceInvoice> - Completed invoice with id, number, status: 'Issued'
Examples:
// Example 1: Simple usage (recommended)
const invoice = await nfe.serviceInvoices.createAndWait('company-id', {
borrower: {
federalTaxNumber: 12345678901,
name: 'João da Silva',
email: '[email protected]',
},
cityServiceCode: '10677',
description: 'Consulting services',
servicesAmount: 1500.00,
});
console.log('Invoice issued:', invoice.number);
console.log('Status:', invoice.status); // 'Issued'
// Example 2: Custom polling configuration
const invoice = await nfe.serviceInvoices.createAndWait('company-id', data, {
pollingInterval: 3000, // Check every 3 seconds
maxWaitTime: 120000, // Wait up to 2 minutes
});
// Example 3: With error handling
try {
const invoice = await nfe.serviceInvoices.createAndWait('company-id', data, {
maxWaitTime: 60000,
});
console.log('✅ Invoice issued successfully');
console.log(` Number: ${invoice.number}`);
console.log(` Amount: R$ ${invoice.servicesAmount}`);
} catch (error) {
if (error.message.includes('timeout')) {
console.error('⏱️ Invoice processing timeout - may complete later');
} else if (error.message.includes('failed')) {
console.error('❌ Invoice processing failed:', error.message);
}
}When to use:
- ✅ You want immediate invoice results without manual polling
- ✅ You can wait 5-30 seconds for processing
- ✅ Simple workflows where async complexity isn't needed
When NOT to use:
- ❌ Background job processing (use
create()+ queue) - ❌ Batch operations (use
createBatch()) - ❌ Need to track processing separately
List service invoices for a company with pagination and filtering.
Parameters:
| Parameter | Type | Description |
|---|---|---|
companyId |
string |
Company ID |
options |
ListOptions |
Filtering and pagination (optional) |
List Options:
| Option | Type | Description |
|---|---|---|
pageCount |
number |
Items per page (default: 25) |
pageIndex |
number |
Page number, 0-indexed (default: 0) |
searchPeriod |
object |
Date range filter |
searchPeriod.startDate |
string |
Start date: 'YYYY-MM-DD' |
searchPeriod.endDate |
string |
End date: 'YYYY-MM-DD' |
Returns: Promise<ServiceInvoice[]> - Array of invoices
Examples:
// Example 1: List all (first page)
const invoices = await nfe.serviceInvoices.list('company-id');
console.log(`Found ${invoices.length} invoices`);
// Example 2: Pagination
const page2 = await nfe.serviceInvoices.list('company-id', {
pageCount: 50, // 50 per page
pageIndex: 1, // Second page (0-indexed)
});
// Example 3: Date filtering
const lastMonth = await nfe.serviceInvoices.list('company-id', {
searchPeriod: {
startDate: '2024-01-01',
endDate: '2024-01-31',
},
pageCount: 100,
});
// Example 4: Process all invoices
let pageIndex = 0;
let allInvoices = [];
while (true) {
const page = await nfe.serviceInvoices.list('company-id', {
pageCount: 100,
pageIndex,
});
allInvoices.push(...page);
if (page.length < 100) break; // Last page
pageIndex++;
}
console.log(`Total invoices: ${allInvoices.length}`);
// Example 5: Find specific invoices
const recentHighValue = await nfe.serviceInvoices.list('company-id', {
searchPeriod: {
startDate: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000)
.toISOString()
.split('T')[0],
endDate: new Date().toISOString().split('T')[0],
},
});
const filtered = recentHighValue.filter(inv => inv.servicesAmount > 5000);
console.log(`High-value invoices: ${filtered.length}`);Get a specific service invoice by ID with complete details.
Parameters:
| Parameter | Type | Description |
|---|---|---|
companyId |
string |
Company ID |
invoiceId |
string |
Invoice ID |
Returns: Promise<ServiceInvoice> - Complete invoice object
Examples:
// Example 1: Basic retrieval
const invoice = await nfe.serviceInvoices.retrieve('company-id', 'invoice-id');
console.log('Invoice:', invoice.number);
console.log('Amount:', invoice.servicesAmount);
console.log('Status:', invoice.status);
console.log('Issued:', invoice.issuedOn);
// Example 2: Check invoice details
const invoice = await nfe.serviceInvoices.retrieve('company-id', 'invoice-id');
console.log('Borrower:', invoice.borrower.name);
console.log('Service:', invoice.description);
console.log('Tax Amount:', invoice.taxes?.iss || 0);
// Example 3: Verify invoice exists before operation
async function sendInvoiceIfExists(companyId, invoiceId) {
try {
const invoice = await nfe.serviceInvoices.retrieve(companyId, invoiceId);
if (invoice.status === 'Issued') {
await nfe.serviceInvoices.sendEmail(companyId, invoiceId, {
emails: [invoice.borrower.email],
});
console.log('Email sent successfully');
} else {
console.log('Invoice not ready:', invoice.status);
}
} catch (error) {
if (error instanceof NotFoundError) {
console.error('Invoice not found');
}
}
}Error Handling:
NotFoundError(404): Invoice or company not foundAuthenticationError(401): Invalid API key
Check invoice processing status (useful for async invoices).
Parameters:
| Parameter | Type | Description |
|---|---|---|
companyId |
string |
Company ID |
invoiceId |
string |
Invoice ID |
Returns: Promise<InvoiceStatus> with:
| Field | Type | Description |
|---|---|---|
status |
string |
Current status (see status values below) |
isComplete |
boolean |
true if processing finished (success or failure) |
isFailed |
boolean |
true if processing failed |
Status Values:
| Status | isComplete | isFailed | Description |
|---|---|---|---|
Issued |
true |
false |
✅ Invoice issued successfully |
IssueFailed |
true |
true |
❌ Issuance failed |
Cancelled |
true |
false |
🚫 Invoice cancelled |
CancellationFailed |
true |
true |
❌ Cancellation failed |
WaitingSend |
false |
false |
⏳ Pending |
WaitingSendAuthorize |
false |
false |
⏳ Awaiting authorization |
Processing |
false |
false |
⏳ Processing |
Examples:
// Example 1: Simple status check
const status = await nfe.serviceInvoices.getStatus('company-id', 'invoice-id');
console.log('Status:', status.status);
console.log('Complete:', status.isComplete ? 'Yes' : 'No');
console.log('Failed:', status.isFailed ? 'Yes' : 'No');
// Example 2: Manual polling loop
async function waitForInvoice(companyId, invoiceId, maxAttempts = 30) {
for (let i = 0; i < maxAttempts; i++) {
const status = await nfe.serviceInvoices.getStatus(companyId, invoiceId);
if (status.isComplete) {
if (status.isFailed) {
throw new Error(`Invoice processing failed: ${status.status}`);
}
console.log('Invoice complete:', status.status);
return await nfe.serviceInvoices.retrieve(companyId, invoiceId);
}
console.log(`Attempt ${i + 1}: ${status.status}`);
await new Promise(resolve => setTimeout(resolve, 2000));
}
throw new Error('Polling timeout');
}
// Example 3: Status-based logic
const status = await nfe.serviceInvoices.getStatus('company-id', 'invoice-id');
if (status.status === 'Issued') {
console.log('✅ Ready to send email');
await nfe.serviceInvoices.sendEmail(/* ... */);
} else if (status.isFailed) {
console.error('❌ Failed:', status.status);
} else {
console.log('⏳ Still processing:', status.status);
}Cancel an issued service invoice.
Parameters:
| Parameter | Type | Description |
|---|---|---|
companyId |
string |
Company ID |
invoiceId |
string |
Invoice ID to cancel |
Returns: Promise<ServiceInvoice> - Cancelled invoice with status: 'Cancelled'
Examples:
// Example 1: Simple cancellation
const cancelled = await nfe.serviceInvoices.cancel('company-id', 'invoice-id');
console.log('Status:', cancelled.status); // 'Cancelled'
// Example 2: Conditional cancellation
const invoice = await nfe.serviceInvoices.retrieve('company-id', 'invoice-id');
if (invoice.status === 'Issued') {
const cancelled = await nfe.serviceInvoices.cancel('company-id', 'invoice-id');
console.log('✅ Invoice cancelled');
} else {
console.log('⚠️ Invoice cannot be cancelled:', invoice.status);
}
// Example 3: Batch cancellation with error handling
const invoiceIds = ['id-1', 'id-2', 'id-3'];
for (const invoiceId of invoiceIds) {
try {
await nfe.serviceInvoices.cancel('company-id', invoiceId);
console.log(`✅ Cancelled: ${invoiceId}`);
} catch (error) {
if (error instanceof NotFoundError) {
console.log(`⚠️ Not found: ${invoiceId}`);
} else {
console.error(`❌ Error cancelling ${invoiceId}:`, error.message);
}
}
}Error Handling:
NotFoundError(404): Invoice not foundValidationError(400): Invoice cannot be cancelled (already cancelled, etc.)
Send invoice via email to specified recipients.
Parameters:
| Parameter | Type | Description |
|---|---|---|
companyId |
string |
Company ID |
invoiceId |
string |
Invoice ID |
options |
SendEmailOptions |
Email configuration |
Email Options:
| Field | Type | Description |
|---|---|---|
emails |
string[] |
Recipient email addresses |
Examples:
// Example 1: Send to single recipient
await nfe.serviceInvoices.sendEmail('company-id', 'invoice-id', {
emails: ['[email protected]'],
});
console.log('✅ Email sent');
// Example 2: Send to multiple recipients
await nfe.serviceInvoices.sendEmail('company-id', 'invoice-id', {
emails: [
'[email protected]',
'[email protected]',
'[email protected]',
],
});
// Example 3: Send after invoice creation
const invoice = await nfe.serviceInvoices.createAndWait('company-id', data);
await nfe.serviceInvoices.sendEmail('company-id', invoice.id, {
emails: [invoice.borrower.email],
});
console.log(`Email sent to ${invoice.borrower.email}`);
// Example 4: Bulk email sending
const invoices = await nfe.serviceInvoices.list('company-id');
for (const invoice of invoices) {
if (invoice.status === 'Issued' && invoice.borrower.email) {
try {
await nfe.serviceInvoices.sendEmail('company-id', invoice.id, {
emails: [invoice.borrower.email],
});
console.log(`✅ Sent: ${invoice.number}`);
} catch (error) {
console.error(`❌ Failed ${invoice.number}:`, error.message);
}
}
}Download invoice PDF. If invoiceId is omitted, downloads all invoices as ZIP.
Parameters:
| Parameter | Type | Description |
|---|---|---|
companyId |
string |
Company ID |
invoiceId |
string |
Invoice ID (optional - omit for bulk ZIP) |
Returns: Promise<Buffer> - PDF file as Buffer (or ZIP for bulk)
Examples:
import { writeFileSync } from 'fs';
// Example 1: Download single invoice PDF
const pdfBuffer = await nfe.serviceInvoices.downloadPdf('company-id', 'invoice-id');
// Validate PDF signature
if (pdfBuffer.toString('utf8', 0, 4) === '%PDF') {
console.log('✅ Valid PDF');
}
// Save to file
writeFileSync('invoice.pdf', pdfBuffer);
console.log('Saved invoice.pdf');
// Example 2: Download all invoices as ZIP
const zipBuffer = await nfe.serviceInvoices.downloadPdf('company-id');
writeFileSync(`invoices_${Date.now()}.zip`, zipBuffer);
console.log('Saved ZIP with all invoices');
// Example 3: Download and send via HTTP response (Express)
app.get('/invoice/:id/pdf', async (req, res) => {
try {
const pdfBuffer = await nfe.serviceInvoices.downloadPdf(
req.user.companyId,
req.params.id
);
res.setHeader('Content-Type', 'application/pdf');
res.setHeader('Content-Disposition', `attachment; filename="invoice-${req.params.id}.pdf"`);
res.send(pdfBuffer);
} catch (error) {
res.status(404).json({ error: 'Invoice not found' });
}
});
// Example 4: Download after creation
const invoice = await nfe.serviceInvoices.createAndWait('company-id', data);
const pdf = await nfe.serviceInvoices.downloadPdf('company-id', invoice.id);
writeFileSync(`invoice_${invoice.number}.pdf`, pdf);
console.log(`Downloaded invoice ${invoice.number}`);Error Handling:
NotFoundError(404): Invoice not ready or not found
Download invoice XML. If invoiceId is omitted, downloads all invoices as ZIP.
Parameters:
| Parameter | Type | Description |
|---|---|---|
companyId |
string |
Company ID |
invoiceId |
string |
Invoice ID (optional - omit for bulk ZIP) |
Returns: Promise<Buffer> - XML file as Buffer (or ZIP for bulk)
Examples:
import { writeFileSync } from 'fs';
// Example 1: Download single invoice XML
const xmlBuffer = await nfe.serviceInvoices.downloadXml('company-id', 'invoice-id');
// Convert Buffer to string
const xmlString = xmlBuffer.toString('utf8');
console.log('XML preview:', xmlString.substring(0, 100));
// Validate XML signature
if (xmlString.startsWith('<?xml')) {
console.log('✅ Valid XML');
}
// Save to file
writeFileSync('invoice.xml', xmlBuffer);
// Example 2: Download all invoices as ZIP
const zipBuffer = await nfe.serviceInvoices.downloadXml('company-id');
writeFileSync(`invoices_xml_${Date.now()}.zip`, zipBuffer);
// Example 3: Parse XML for integration
import { parseString } from 'xml2js';
const xmlBuffer = await nfe.serviceInvoices.downloadXml('company-id', 'invoice-id');
const xmlString = xmlBuffer.toString('utf8');
parseString(xmlString, (err, result) => {
if (err) throw err;
console.log('Parsed XML:', result);
// Process structured data
});
// Example 4: Bulk download and extract
const zipBuffer = await nfe.serviceInvoices.downloadXml('company-id');
writeFileSync('invoices.zip', zipBuffer);
// Extract ZIP using library like 'adm-zip'
// const AdmZip = require('adm-zip');
// const zip = new AdmZip(zipBuffer);
// zip.extractAllTo('./invoices/', true);createBatch(companyId: string, invoicesData: ServiceInvoiceData[], options?: BatchOptions): Promise<ServiceInvoice[]>
Create multiple invoices concurrently with concurrency control.
Parameters:
| Parameter | Type | Description |
|---|---|---|
companyId |
string |
Company ID |
invoicesData |
ServiceInvoiceData[] |
Array of invoice data |
options |
BatchOptions |
Batch configuration (optional) |
Batch Options:
| Option | Type | Default | Description |
|---|---|---|---|
waitForComplete |
boolean |
true |
Wait for all invoices to complete processing |
maxConcurrent |
number |
5 |
Maximum concurrent requests |
pollingInterval |
number |
2000 |
Polling interval in ms (if waitForComplete=true) |
maxWaitTime |
number |
60000 |
Max wait time per invoice in ms |
Returns: Promise<ServiceInvoice[]> - Array of created invoices
Examples:
// Example 1: Basic batch creation
const invoicesData = [
{
borrower: { federalTaxNumber: 111, name: 'Client 1', email: '[email protected]' },
cityServiceCode: '10677',
description: 'Service 1',
servicesAmount: 1000,
},
{
borrower: { federalTaxNumber: 222, name: 'Client 2', email: '[email protected]' },
cityServiceCode: '10677',
description: 'Service 2',
servicesAmount: 2000,
},
{
borrower: { federalTaxNumber: 333, name: 'Client 3', email: '[email protected]' },
cityServiceCode: '10677',
description: 'Service 3',
servicesAmount: 3000,
},
];
const invoices = await nfe.serviceInvoices.createBatch('company-id', invoicesData);
console.log(`Created ${invoices.length} invoices`);
invoices.forEach(inv => console.log(`- ${inv.number}: R$ ${inv.servicesAmount}`));
// Example 2: Custom concurrency
const invoices = await nfe.serviceInvoices.createBatch('company-id', invoicesData, {
maxConcurrent: 3, // Process 3 at a time
waitForComplete: true, // Wait for all to finish
});
// Example 3: Batch without waiting (fire and forget)
const results = await nfe.serviceInvoices.createBatch('company-id', invoicesData, {
waitForComplete: false, // Don't wait for async processing
maxConcurrent: 10,
});
// Results may contain async processing info (location, flowStatus)
results.forEach(result => {
if ('id' in result) {
console.log(`Issued: ${result.id}`);
} else {
console.log(`Processing: ${result.location}`);
}
});
// Example 4: Read from CSV and batch create
import { parse } from 'csv-parse/sync';
import { readFileSync } from 'fs';
const csv = readFileSync('invoices.csv', 'utf8');
const records = parse(csv, { columns: true });
const invoicesData = records.map(row => ({
borrower: {
federalTaxNumber: parseInt(row.cpf),
name: row.name,
email: row.email,
},
cityServiceCode: row.serviceCode,
description: row.description,
servicesAmount: parseFloat(row.amount),
}));
console.log(`Creating ${invoicesData.length} invoices from CSV...`);
const invoices = await nfe.serviceInvoices.createBatch('company-id', invoicesData, {
maxConcurrent: 5,
waitForComplete: true,
});
console.log(`✅ Created ${invoices.length} invoices`);
// Example 5: Error handling in batch
try {
const invoices = await nfe.serviceInvoices.createBatch('company-id', invoicesData);
console.log('All succeeded');
} catch (error) {
console.error('Batch creation failed:', error.message);
// Note: Some invoices may have been created successfully
// Check partial results if needed
}Performance Notes:
- Default concurrency (5) is safe for most APIs
- Increase
maxConcurrentcarefully to avoid rate limiting - Large batches (>100 invoices) should be split into chunks
- Use
waitForComplete: falsefor background processing
ServiceInvoice:
interface ServiceInvoice {
id: string;
number?: string;
status: 'Issued' | 'Cancelled' | 'Processing' | 'IssueFailed';
flowStatus?: string;
flowMessage?: string;
borrower: {
federalTaxNumber: number;
name: string;
email?: string;
address?: Address;
};
cityServiceCode: string;
description: string;
servicesAmount: number;
deductions?: number;
discountUnconditioned?: number;
discountConditioned?: number;
issuedOn?: string;
rpsSerialNumber?: string;
rpsNumber?: number;
taxes?: {
retainIss?: boolean;
iss?: number;
pis?: number;
cofins?: number;
inss?: number;
ir?: number;
csll?: number;
};
}Resource: nfe.companies
Company management operations including CRUD, certificate management, and search capabilities.
Create a new company with automatic CNPJ/CPF validation.
const company = await nfe.companies.create({
federalTaxNumber: 12345678000190, // CNPJ (14 digits) or CPF (11 digits)
name: 'My Company',
email: '[email protected]',
address: {
country: 'BRA',
postalCode: '01310-100',
street: 'Av. Paulista',
number: '1000',
city: {
code: '3550308',
name: 'São Paulo'
},
state: 'SP'
}
});List companies with pagination.
const companies = await nfe.companies.list({
pageCount: 20,
pageIndex: 0
});Get all companies (auto-pagination).
// Automatically fetches all pages
const allCompanies = await nfe.companies.listAll();
console.log(`Total: ${allCompanies.length} companies`);Memory-efficient streaming of companies.
// Process companies one at a time
for await (const company of nfe.companies.listIterator()) {
console.log(company.name);
// Process company...
}Get a specific company by ID.
const company = await nfe.companies.retrieve('company-id');
console.log(company.name);Update company information with validation.
const updated = await nfe.companies.update('company-id', {
name: 'New Company Name',
email: '[email protected]'
});Delete a company (named remove to avoid JS keyword conflict).
const result = await nfe.companies.remove('company-id');
console.log(`Deleted: ${result.deleted}`);Pre-validate a certificate before upload.
import { readFile } from 'fs/promises';
const certBuffer = await readFile('./certificate.pfx');
const validation = await nfe.companies.validateCertificate(certBuffer, 'password');
if (validation.valid) {
console.log('Valid until:', validation.metadata?.validTo);
} else {
console.error('Invalid:', validation.error);
}uploadCertificate(companyId: string, data: CertificateData): Promise<{ uploaded: boolean; message?: string }>
Upload digital certificate with automatic validation.
import { readFile } from 'fs/promises';
const certBuffer = await readFile('./certificate.pfx');
const result = await nfe.companies.uploadCertificate('company-id', {
file: certBuffer,
password: 'certificate-password',
filename: 'certificate.pfx' // Optional
});
console.log(result.message);Get certificate status with expiration calculation.
const status = await nfe.companies.getCertificateStatus('company-id');
console.log('Has certificate:', status.hasCertificate);
console.log('Expires on:', status.expiresOn);
console.log('Days until expiration:', status.daysUntilExpiration);
if (status.isExpiringSoon) {
console.warn('⚠️ Certificate expiring soon!');
}replaceCertificate(companyId: string, data: CertificateData): Promise<{ uploaded: boolean; message?: string }>
Replace existing certificate (alias for upload).
const result = await nfe.companies.replaceCertificate('company-id', {
file: newCertBuffer,
password: 'new-password',
filename: 'new-certificate.pfx'
});checkCertificateExpiration(companyId: string, thresholdDays?: number): Promise<ExpirationWarning | null>
Check if certificate is expiring soon.
// Check with 30-day threshold (default)
const warning = await nfe.companies.checkCertificateExpiration('company-id', 30);
if (warning) {
console.warn(`Certificate expiring in ${warning.daysRemaining} days`);
console.log('Expires on:', warning.expiresOn);
}Find company by federal tax number (CNPJ or CPF).
// Search by CNPJ (14 digits)
const company = await nfe.companies.findByTaxNumber(12345678000190);
// Or by CPF (11 digits)
const company = await nfe.companies.findByTaxNumber(12345678901);
if (company) {
console.log('Found:', company.name);
} else {
console.log('Company not found');
}Find companies by name (case-insensitive partial match).
// Find all companies with "Acme" in the name
const companies = await nfe.companies.findByName('Acme');
companies.forEach(company => {
console.log(`Match: ${company.name}`);
});Get all companies that have valid certificates.
const companiesWithCerts = await nfe.companies.getCompaniesWithCertificates();
console.log(`${companiesWithCerts.length} companies with valid certificates`);Get companies with expiring certificates.
// Find companies with certificates expiring within 30 days
const expiring = await nfe.companies.getCompaniesWithExpiringCertificates(30);
expiring.forEach(company => {
console.warn(`⚠️ ${company.name} certificate expiring soon`);
});The CertificateValidator utility can also be used independently:
import { CertificateValidator } from 'nfe-io';
// Check if format is supported
if (CertificateValidator.isSupportedFormat('cert.pfx')) {
console.log('✓ Supported format');
}
// Calculate days until expiration
const expirationDate = new Date('2026-12-31');
const days = CertificateValidator.getDaysUntilExpiration(expirationDate);
console.log(`Days remaining: ${days}`);
// Check if expiring soon
if (CertificateValidator.isExpiringSoon(expirationDate, 30)) {
console.warn('Certificate expiring within 30 days!');
}Resource: nfe.legalPeople
Legal person (PJ/CNPJ) operations, scoped by company.
Create a legal person.
const legalPerson = await nfe.legalPeople.create('company-id', {
federalTaxNumber: '12345678000190',
name: 'Legal Person Company',
email: '[email protected]'
});List legal people for a company.
const people = await nfe.legalPeople.list('company-id');Get a specific legal person.
const person = await nfe.legalPeople.retrieve('company-id', 'person-id');Update legal person information.
const updated = await nfe.legalPeople.update('company-id', 'person-id', {
name: 'Updated Name'
});Delete a legal person.
await nfe.legalPeople.delete('company-id', 'person-id');Find legal person by CNPJ.
const person = await nfe.legalPeople.findByTaxNumber('company-id', '12345678000190');Resource: nfe.naturalPeople
Natural person (PF/CPF) operations, scoped by company.
Create a natural person.
const person = await nfe.naturalPeople.create('company-id', {
federalTaxNumber: '12345678901',
name: 'John Doe',
email: '[email protected]'
});List natural people for a company.
const people = await nfe.naturalPeople.list('company-id');Get a specific natural person.
const person = await nfe.naturalPeople.retrieve('company-id', 'person-id');Update natural person information.
const updated = await nfe.naturalPeople.update('company-id', 'person-id', {
name: 'Jane Doe'
});Delete a natural person.
await nfe.naturalPeople.delete('company-id', 'person-id');Find natural person by CPF.
const person = await nfe.naturalPeople.findByTaxNumber('company-id', '12345678901');Resource: nfe.webhooks
Webhook configuration and management.
Create a webhook.
const webhook = await nfe.webhooks.create({
url: 'https://example.com/webhook',
events: ['invoice.issued', 'invoice.cancelled'],
secret: 'webhook-secret'
});List all webhooks.
const webhooks = await nfe.webhooks.list();Get a specific webhook.
const webhook = await nfe.webhooks.retrieve('webhook-id');Update webhook configuration.
const updated = await nfe.webhooks.update('webhook-id', {
events: ['invoice.issued', 'invoice.cancelled', 'invoice.error']
});Delete a webhook.
await nfe.webhooks.delete('webhook-id');Validate webhook signature (HMAC SHA-256).
// In your webhook endpoint
app.post('/webhook', (req, res) => {
const signature = req.headers['x-nfe-signature'];
const payload = JSON.stringify(req.body);
const isValid = nfe.webhooks.validateSignature(
payload,
signature,
'your-webhook-secret'
);
if (!isValid) {
return res.status(401).send('Invalid signature');
}
// Process webhook...
});Test webhook delivery.
await nfe.webhooks.test('webhook-id');Get list of available webhook event types.
const events = await nfe.webhooks.getAvailableEvents();
// ['invoice.issued', 'invoice.cancelled', ...]Resource: nfe.transportationInvoices
Manage CT-e (Conhecimento de Transporte Eletrônico) documents via SEFAZ Distribuição DFe.
Note: This resource uses a separate API host (
api.nfse.io). You can configure a specific API key withdataApiKey, or the SDK will useapiKeyas fallback.
Prerequisites:
- Company must be registered with a valid A1 digital certificate
- Webhook must be configured to receive CT-e notifications
enable(companyId: string, options?: EnableTransportationInvoiceOptions): Promise<TransportationInvoiceInboundSettings>
Enable automatic CT-e search for a company.
// Enable with default settings
const settings = await nfe.transportationInvoices.enable('company-id');
// Enable starting from a specific NSU
const settings = await nfe.transportationInvoices.enable('company-id', {
startFromNsu: 12345
});
// Enable starting from a specific date
const settings = await nfe.transportationInvoices.enable('company-id', {
startFromDate: '2024-01-01T00:00:00Z'
});Options:
| Property | Type | Description |
|---|---|---|
startFromNsu |
number |
Start searching from this NSU number |
startFromDate |
string |
Start searching from this date (ISO 8601) |
Disable automatic CT-e search for a company.
const settings = await nfe.transportationInvoices.disable('company-id');
console.log('Status:', settings.status); // 'Disabled'Get current automatic CT-e search settings.
const settings = await nfe.transportationInvoices.getSettings('company-id');
console.log('Status:', settings.status);
console.log('Start NSU:', settings.startFromNsu);
console.log('Created:', settings.createdOn);Response:
| Property | Type | Description |
|---|---|---|
status |
string |
Current status ('Active', 'Disabled', etc.) |
startFromNsu |
number |
Starting NSU number |
startFromDate |
string |
Starting date (if configured) |
createdOn |
string |
Creation timestamp |
modifiedOn |
string |
Last modification timestamp |
Retrieve CT-e metadata by its 44-digit access key.
const cte = await nfe.transportationInvoices.retrieve(
'company-id',
'35240112345678000190570010000001231234567890'
);
console.log('Sender:', cte.nameSender);
console.log('CNPJ:', cte.federalTaxNumberSender);
console.log('Amount:', cte.totalInvoiceAmount);
console.log('Issued:', cte.issuedOn);Response:
| Property | Type | Description |
|---|---|---|
accessKey |
string |
44-digit access key |
type |
string |
Document type |
status |
string |
Document status |
nameSender |
string |
Sender company name |
federalTaxNumberSender |
string |
Sender CNPJ |
totalInvoiceAmount |
number |
Total invoice amount |
issuedOn |
string |
Issue date |
receivedOn |
string |
Receipt date |
Download CT-e XML content.
const xml = await nfe.transportationInvoices.downloadXml(
'company-id',
'35240112345678000190570010000001231234567890'
);
fs.writeFileSync('cte.xml', xml);getEvent(companyId: string, accessKey: string, eventKey: string): Promise<TransportationInvoiceMetadata>
Retrieve CT-e event metadata.
const event = await nfe.transportationInvoices.getEvent(
'company-id',
'35240112345678000190570010000001231234567890',
'event-key-123'
);
console.log('Event type:', event.type);
console.log('Event status:', event.status);Download CT-e event XML content.
const eventXml = await nfe.transportationInvoices.downloadEventXml(
'company-id',
'35240112345678000190570010000001231234567890',
'event-key-123'
);
fs.writeFileSync('cte-event.xml', eventXml);Resource: nfe.inboundProductInvoices
Query NF-e (Nota Fiscal Eletrônica de Produto) documents received via SEFAZ Distribuição NF-e.
Note: This resource uses a separate API host (
api.nfse.io). You can configure a specific API key withdataApiKey, or the SDK will useapiKeyas fallback.
Prerequisites:
- Company must be registered with a valid A1 digital certificate
- Webhook must be configured to receive NF-e notifications
Enable automatic NF-e inbound fetching for a company.
// Enable with production environment and webhook v2
const settings = await nfe.inboundProductInvoices.enableAutoFetch('company-id', {
environmentSEFAZ: 'Production',
webhookVersion: '2',
});
// Enable starting from a specific NSU
const settings = await nfe.inboundProductInvoices.enableAutoFetch('company-id', {
startFromNsu: '999999',
environmentSEFAZ: 'Production',
});
// Enable with automatic manifesting
const settings = await nfe.inboundProductInvoices.enableAutoFetch('company-id', {
environmentSEFAZ: 'Production',
automaticManifesting: { minutesToWaitAwarenessOperation: '30' },
});Options:
| Property | Type | Description |
|---|---|---|
startFromNsu |
string |
Start searching from this NSU number |
startFromDate |
string |
Start searching from this date (ISO 8601) |
environmentSEFAZ |
string | null |
SEFAZ environment ('Production', etc.) |
automaticManifesting |
AutomaticManifesting |
Auto-manifest configuration |
webhookVersion |
string |
Webhook version ('1' or '2') |
Disable automatic NF-e inbound fetching for a company.
const settings = await nfe.inboundProductInvoices.disableAutoFetch('company-id');
console.log('Status:', settings.status); // 'Inactive'Get current automatic NF-e inbound settings.
const settings = await nfe.inboundProductInvoices.getSettings('company-id');
console.log('Status:', settings.status);
console.log('Environment:', settings.environmentSEFAZ);
console.log('Webhook version:', settings.webhookVersion);
console.log('Start NSU:', settings.startFromNsu);Response (InboundSettings):
| Property | Type | Description |
|---|---|---|
status |
string |
Current status ('Active', 'Inactive', etc.) |
startFromNsu |
string |
Starting NSU number |
startFromDate |
string |
Starting date (if configured) |
environmentSEFAZ |
string | null |
SEFAZ environment |
automaticManifesting |
AutomaticManifesting |
Auto-manifest configuration |
webhookVersion |
string |
Webhook version |
companyId |
string |
Company ID |
createdOn |
string |
Creation timestamp |
modifiedOn |
string |
Last modification timestamp |
Retrieve NF-e metadata by 44-digit access key (webhook v1 format).
const nfeDoc = await nfe.inboundProductInvoices.getDetails(
'company-id',
'35240112345678000190550010000001231234567890'
);
console.log('Issuer:', nfeDoc.issuer?.name);
console.log('Amount:', nfeDoc.totalInvoiceAmount);
console.log('Issued:', nfeDoc.issuedOn);getProductInvoiceDetails(companyId: string, accessKey: string): Promise<InboundProductInvoiceMetadata>
Retrieve NF-e metadata by 44-digit access key (webhook v2 format, recommended).
const nfeDoc = await nfe.inboundProductInvoices.getProductInvoiceDetails(
'company-id',
'35240112345678000190550010000001231234567890'
);
console.log('Issuer:', nfeDoc.issuer?.name);
console.log('Amount:', nfeDoc.totalInvoiceAmount);
console.log('Product invoices:', nfeDoc.productInvoices?.length);Response (InboundInvoiceMetadata / InboundProductInvoiceMetadata):
| Property | Type | Description |
|---|---|---|
id |
string |
Document ID |
accessKey |
string |
44-digit access key |
nsu |
string |
NSU number |
nfeNumber |
string |
NF-e number |
issuer |
InboundIssuer |
Issuer information |
buyer |
InboundBuyer |
Buyer information |
totalInvoiceAmount |
string |
Total amount |
issuedOn |
string |
Issue date |
description |
string |
Description |
links |
InboundLinks |
XML/PDF download links |
productInvoices |
InboundProductInvoice[] |
Product invoices (v2 only) |
getEventDetails(companyId: string, accessKey: string, eventKey: string): Promise<InboundInvoiceMetadata>
Retrieve NF-e event metadata (webhook v1 format).
const event = await nfe.inboundProductInvoices.getEventDetails(
'company-id',
'35240112345678000190550010000001231234567890',
'event-key-123'
);getProductInvoiceEventDetails(companyId: string, accessKey: string, eventKey: string): Promise<InboundProductInvoiceMetadata>
Retrieve NF-e event metadata (webhook v2 format).
const event = await nfe.inboundProductInvoices.getProductInvoiceEventDetails(
'company-id',
'35240112345678000190550010000001231234567890',
'event-key-123'
);Download NF-e XML content.
const xml = await nfe.inboundProductInvoices.getXml(
'company-id',
'35240112345678000190550010000001231234567890'
);
fs.writeFileSync('nfe.xml', xml);Download NF-e event XML content.
const eventXml = await nfe.inboundProductInvoices.getEventXml(
'company-id',
'35240112345678000190550010000001231234567890',
'event-key-123'
);
fs.writeFileSync('nfe-event.xml', eventXml);Download NF-e PDF (DANFE).
const pdf = await nfe.inboundProductInvoices.getPdf(
'company-id',
'35240112345678000190550010000001231234567890'
);Get NF-e data in JSON format.
const json = await nfe.inboundProductInvoices.getJson(
'company-id',
'35240112345678000190550010000001231234567890'
);Send a manifest event for an NF-e. Defaults to 210210 (Ciência da Operação).
// Ciência da Operação (default)
await nfe.inboundProductInvoices.manifest(
'company-id',
'35240112345678000190550010000001231234567890'
);
// Confirmação da Operação
await nfe.inboundProductInvoices.manifest(
'company-id',
'35240112345678000190550010000001231234567890',
210220
);
// Operação não Realizada
await nfe.inboundProductInvoices.manifest(
'company-id',
'35240112345678000190550010000001231234567890',
210240
);Manifest Event Types:
| Code | Event |
|---|---|
210210 |
Ciência da Operação (awareness, default) |
210220 |
Confirmação da Operação (confirmation) |
210240 |
Operação não Realizada (operation not performed) |
Reprocess a webhook notification by access key or NSU.
// By access key
await nfe.inboundProductInvoices.reprocessWebhook(
'company-id',
'35240112345678000190550010000001231234567890'
);
// By NSU
await nfe.inboundProductInvoices.reprocessWebhook(
'company-id',
'12345'
);Resource: nfe.productInvoiceQuery
Query NF-e (Nota Fiscal Eletrônica) product invoices directly on SEFAZ by access key. This is a read-only resource that does not require company scope.
Note: This resource uses a separate API host (
nfe.api.nfe.io). You can configure a specific API key withdataApiKey, or the SDK will useapiKeyas fallback.
Retrieve full product invoice details from SEFAZ by access key.
const invoice = await nfe.productInvoiceQuery.retrieve(
'35240112345678000190550010000001231234567890'
);
console.log(invoice.currentStatus); // 'authorized'
console.log(invoice.issuer?.name);
console.log(invoice.totals?.icms?.invoiceAmount);Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
accessKey |
string |
Yes | 44-digit numeric access key (Chave de Acesso) |
Returns: ProductInvoiceDetails — Full invoice details including issuer, buyer, items, totals, transport, and payment.
Throws:
ValidationErrorif access key format is invalidNotFoundErrorif no invoice matches the access key (HTTP 404)AuthenticationErrorif API key is invalid (HTTP 401)
Download the DANFE PDF for a product invoice.
const pdfBuffer = await nfe.productInvoiceQuery.downloadPdf(
'35240112345678000190550010000001231234567890'
);
fs.writeFileSync('danfe.pdf', pdfBuffer);Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
accessKey |
string |
Yes | 44-digit numeric access key |
Returns: Buffer containing the PDF binary content.
Download the raw NF-e XML for a product invoice.
const xmlBuffer = await nfe.productInvoiceQuery.downloadXml(
'35240112345678000190550010000001231234567890'
);
fs.writeFileSync('nfe.xml', xmlBuffer);Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
accessKey |
string |
Yes | 44-digit numeric access key |
Returns: Buffer containing the XML binary content.
List fiscal events (cancellations, corrections, manifestations) for a product invoice.
const result = await nfe.productInvoiceQuery.listEvents(
'35240112345678000190550010000001231234567890'
);
for (const event of result.events ?? []) {
console.log(event.description, event.authorizedOn);
}Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
accessKey |
string |
Yes | 44-digit numeric access key |
Returns: ProductInvoiceEventsResponse with an array of fiscal events and query timestamp.
Resource: nfe.consumerInvoiceQuery
Query CFe-SAT (Cupom Fiscal Eletrônico) consumer invoices by access key. This is a read-only resource that does not require company scope.
Note: This resource uses a separate API host (
nfe.api.nfe.io). You can configure a specific API key withdataApiKey, or the SDK will useapiKeyas fallback.
Retrieve full CFe-SAT coupon details by access key.
const coupon = await nfe.consumerInvoiceQuery.retrieve(
'35240112345678000190590000000012341234567890'
);
console.log(coupon.currentStatus); // 'Authorized'
console.log(coupon.issuer?.name);
console.log(coupon.totals?.couponAmount);Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
accessKey |
string |
Yes | 44-digit numeric access key (Chave de Acesso) |
Returns: TaxCoupon — Full coupon details including issuer, buyer, items, totals, and payment.
Throws:
ValidationErrorif access key format is invalidNotFoundErrorif no coupon matches the access key (HTTP 404)AuthenticationErrorif API key is invalid (HTTP 401)
Download the raw CFe XML for a consumer invoice.
const xmlBuffer = await nfe.consumerInvoiceQuery.downloadXml(
'35240112345678000190590000000012341234567890'
);
fs.writeFileSync('cfe.xml', xmlBuffer);Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
accessKey |
string |
Yes | 44-digit numeric access key |
Returns: Buffer containing the XML binary content.
Resource: nfe.legalEntityLookup
Query Brazilian company (CNPJ) data from Receita Federal and state tax registries (SEFAZ). This is a read-only resource that does not require company scope.
Note: This resource uses a separate API host (
legalentity.api.nfe.io). You can configure a specific API key withdataApiKey, or the SDK will useapiKeyas fallback.
getBasicInfo(federalTaxNumber: string, options?: LegalEntityBasicInfoOptions): Promise<LegalEntityBasicInfoResponse>
Lookup basic company information by CNPJ from Receita Federal. Returns legal name, trade name, address, phone numbers, economic activities (CNAE), legal nature, partners, and registration status.
const result = await nfe.legalEntityLookup.getBasicInfo('12.345.678/0001-90');
console.log(result.legalEntity?.name); // 'EMPRESA LTDA'
console.log(result.legalEntity?.status); // 'Active'
console.log(result.legalEntity?.address?.city?.name);
// With options
const result = await nfe.legalEntityLookup.getBasicInfo('12345678000190', {
updateAddress: false,
updateCityCode: true,
});Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
federalTaxNumber |
string |
Yes | CNPJ, with or without punctuation (e.g., "12345678000190" or "12.345.678/0001-90") |
options |
LegalEntityBasicInfoOptions |
No | Lookup options |
Options:
| Property | Type | Default | Description |
|---|---|---|---|
updateAddress |
boolean |
true |
Update address from postal service data |
updateCityCode |
boolean |
false |
Update only the city IBGE code when updateAddress is false |
Returns: LegalEntityBasicInfoResponse — Company basic information including address, phones, activities, partners.
Throws:
ValidationErrorif CNPJ format is invalid (not 14 digits after stripping punctuation)NotFoundErrorif no company found for the given CNPJ (HTTP 404)AuthenticationErrorif API key is invalid (HTTP 401)
Lookup state tax registration (Inscrição Estadual) by CNPJ and state. Returns tax regime, legal nature, and state tax registration details including fiscal document indicators (NFe, NFSe, CTe, NFCe).
const result = await nfe.legalEntityLookup.getStateTaxInfo('SP', '12345678000190');
console.log(result.legalEntity?.taxRegime); // 'SimplesNacional'
for (const tax of result.legalEntity?.stateTaxes ?? []) {
console.log(`IE: ${tax.taxNumber} — Status: ${tax.status}`);
console.log(` NFe: ${tax.nfe?.status}, NFSe: ${tax.nfse?.status}`);
}Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
state |
string |
Yes | Brazilian state code (e.g., "SP", "RJ", "MG"). Case-insensitive. |
federalTaxNumber |
string |
Yes | CNPJ, with or without punctuation |
Returns: LegalEntityStateTaxResponse — State tax registration information.
Throws:
ValidationErrorif state code is invalid or CNPJ format is invalidAuthenticationErrorif API key is invalid (HTTP 401)
getStateTaxForInvoice(state: string, federalTaxNumber: string): Promise<LegalEntityStateTaxForInvoiceResponse>
Evaluate state tax registration for invoice issuance. Returns extended status information (including UnabledTemp, UnabledNotConfirmed) useful for determining whether product invoices (NF-e) can be issued.
const result = await nfe.legalEntityLookup.getStateTaxForInvoice('MG', '12345678000190');
for (const tax of result.legalEntity?.stateTaxes ?? []) {
if (tax.status === 'Abled') {
console.log(`Can issue invoices with IE: ${tax.taxNumber}`);
}
}Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
state |
string |
Yes | Brazilian state code. Case-insensitive. |
federalTaxNumber |
string |
Yes | CNPJ, with or without punctuation |
Returns: LegalEntityStateTaxForInvoiceResponse — State tax data with extended status for invoice evaluation.
Throws:
ValidationErrorif state code is invalid or CNPJ format is invalidAuthenticationErrorif API key is invalid (HTTP 401)
getSuggestedStateTaxForInvoice(state: string, federalTaxNumber: string): Promise<LegalEntityStateTaxForInvoiceResponse>
Get the best (suggested) state tax registration for invoice issuance. When multiple registrations are enabled in a state, NFE.io applies evaluation criteria to recommend the optimal IE.
const result = await nfe.legalEntityLookup.getSuggestedStateTaxForInvoice('SP', '12345678000190');
const bestIE = result.legalEntity?.stateTaxes?.[0];
if (bestIE) {
console.log(`Recommended IE: ${bestIE.taxNumber} (${bestIE.status})`);
}Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
state |
string |
Yes | Brazilian state code. Case-insensitive. |
federalTaxNumber |
string |
Yes | CNPJ, with or without punctuation |
Returns: LegalEntityStateTaxForInvoiceResponse — Suggested state tax data prioritized by NFE.io criteria.
Throws:
ValidationErrorif state code is invalid or CNPJ format is invalidAuthenticationErrorif API key is invalid (HTTP 401)
type BrazilianState =
| 'AC' | 'AL' | 'AM' | 'AP' | 'BA' | 'CE' | 'DF' | 'ES' | 'GO'
| 'MA' | 'MG' | 'MS' | 'MT' | 'PA' | 'PB' | 'PE' | 'PI' | 'PR'
| 'RJ' | 'RN' | 'RO' | 'RR' | 'RS' | 'SC' | 'SE' | 'SP' | 'TO'
| 'EX' | 'NA';
interface LegalEntityBasicInfoOptions {
updateAddress?: boolean;
updateCityCode?: boolean;
}
interface LegalEntityBasicInfoResponse {
legalEntity?: LegalEntityBasicInfo;
}
interface LegalEntityBasicInfo {
tradeName?: string;
name?: string;
federalTaxNumber?: number;
size?: 'Unknown' | 'ME' | 'EPP' | 'DEMAIS';
openedOn?: string;
address?: LegalEntityAddress;
phones?: LegalEntityPhone[];
status?: 'Unknown' | 'Active' | 'Suspended' | 'Cancelled' | 'Unabled' | 'Null';
email?: string;
shareCapital?: number;
economicActivities?: LegalEntityEconomicActivity[];
legalNature?: LegalEntityNature;
partners?: LegalEntityPartner[];
unit?: 'Headoffice' | 'Subsidiary';
// ... and more fields
}
interface LegalEntityStateTaxResponse {
legalEntity?: LegalEntityStateTaxInfo;
}
interface LegalEntityStateTaxForInvoiceResponse {
legalEntity?: LegalEntityStateTaxForInvoiceInfo;
}
interface LegalEntityStateTax {
status?: 'Abled' | 'Unabled' | 'Cancelled' | 'Unknown';
taxNumber?: string;
code?: BrazilianState;
nfe?: LegalEntityFiscalDocumentInfo;
nfse?: LegalEntityFiscalDocumentInfo;
cte?: LegalEntityFiscalDocumentInfo;
nfce?: LegalEntityFiscalDocumentInfo;
// ... and more fields
}
interface LegalEntityStateTaxForInvoice {
status?: 'Abled' | 'Unabled' | 'Cancelled' | 'UnabledTemp' | 'UnabledNotConfirmed'
| 'Unknown' | 'UnknownTemp' | 'UnknownNotConfirmed';
taxNumber?: string;
// ... same structure as LegalEntityStateTax with extended status
}See src/core/types.ts for the complete type definitions.
Resource: nfe.naturalPersonLookup
API Host: naturalperson.api.nfe.io
Authentication: Uses dataApiKey (falls back to apiKey)
Lookup CPF cadastral status (situação cadastral) at the Brazilian Federal Revenue Service (Receita Federal).
Query the cadastral status of a CPF, returning the person's name, CPF, birth date, status, and query timestamp.
// With string date
const result = await nfe.naturalPersonLookup.getStatus('123.456.789-01', '1990-01-15');
console.log(result.name); // 'JOÃO DA SILVA'
console.log(result.status); // 'Regular'
// With Date object
const result = await nfe.naturalPersonLookup.getStatus('12345678901', new Date(1990, 0, 15));Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
federalTaxNumber |
string |
Yes | CPF number, with or without punctuation (e.g., "12345678901" or "123.456.789-01") |
birthDate |
string | Date |
Yes | Date of birth in YYYY-MM-DD format or a Date object |
Returns: NaturalPersonStatusResponse — CPF cadastral status data.
Throws:
ValidationErrorif CPF format is invalid (not 11 digits) or birth date format is invalidNotFoundErrorif CPF is not found or birth date does not match (HTTP 404)AuthenticationErrorif API key is invalid (HTTP 401)
type NaturalPersonStatus =
| 'Regular'
| 'Suspensa'
| 'Cancelada'
| 'Titular Falecido'
| 'Pendente de Regularização'
| 'Nula'
| (string & {});
interface NaturalPersonStatusResponse {
name?: string;
federalTaxNumber: string;
birthOn?: string;
status?: NaturalPersonStatus;
createdOn?: string;
}See src/core/types.ts for the complete type definitions.
Resource: nfe.taxCalculation
API Host: api.nfse.io
Authentication: Uses dataApiKey (falls back to apiKey)
Compute all applicable Brazilian taxes (ICMS, ICMS-ST, PIS, COFINS, IPI, II) for product operations using the Tax Calculation Engine (Motor de Cálculo de Tributos).
Submit an operation with issuer, recipient, operation type, and product items to compute per-item tax breakdowns.
const result = await nfe.taxCalculation.calculate('tenant-id', {
operationType: 'Outgoing',
issuer: { state: 'SP', taxRegime: 'RealProfit' },
recipient: { state: 'RJ' },
items: [{
id: 'item-1',
operationCode: 121,
origin: 'National',
ncm: '61091000',
quantity: 10,
unitAmount: 100.00
}]
});
for (const item of result.items ?? []) {
console.log(`Item ${item.id}: CFOP ${item.cfop}`);
console.log(` ICMS CST: ${item.icms?.cst}, value: ${item.icms?.vICMS}`);
}Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
tenantId |
string |
Yes | Subscription/account ID that scopes the tax rules |
request |
CalculateRequest |
Yes | Tax calculation request payload |
Returns: CalculateResponse — Per-item tax breakdowns including CFOP, ICMS, PIS, COFINS, IPI, II.
Throws:
ValidationErroriftenantIdis emptyValidationErrorif required fields are missing (issuer, recipient, operationType, items)AuthenticationErrorif API key is invalid (HTTP 401)BadRequestErrorif the API rejects the payload (HTTP 400)
type TaxOperationType = 'Outgoing' | 'Incoming';
type TaxOrigin =
| 'National' | 'ForeignDirectImport' | 'ForeignInternalMarket'
| 'NationalWith40To70Import' | 'NationalPpb' | 'NationalWithLess40Import'
| 'ForeignDirectImportWithoutNationalSimilar'
| 'ForeignInternalMarketWithoutNationalSimilar'
| 'NationalWithGreater70Import';
type TaxCalcTaxRegime =
| 'NationalSimple' | 'RealProfit' | 'PresumedProfit'
| 'NationalSimpleSublimitExceeded' | 'IndividualMicroEnterprise' | 'Exempt';
interface CalculateRequest {
collectionId?: string;
issuer: CalculateRequestIssuer; // required: state, taxRegime
recipient: CalculateRequestRecipient; // required: state
operationType: TaxOperationType;
items: CalculateItemRequest[]; // required: id, operationCode, origin, quantity, unitAmount
isProductRegistration?: boolean;
}
interface CalculateResponse {
items?: CalculateItemResponse[]; // per-item: cfop, icms, pis, cofins, ipi, ii, icmsUfDest
}See src/core/types.ts for the complete type definitions including all tax component interfaces (TaxIcms, TaxPis, TaxCofins, TaxIpi, TaxIi, TaxIcmsUfDest).
Resource: nfe.taxCodes
API Host: api.nfse.io
Authentication: Uses dataApiKey (falls back to apiKey)
Paginated listings of auxiliary tax code reference tables needed as inputs for the Tax Calculation Engine.
List operation codes (natureza de operação) — e.g., 121 = "Venda de mercadoria".
const result = await nfe.taxCodes.listOperationCodes({ pageIndex: 1, pageCount: 20 });
console.log(`Total: ${result.totalCount}, Page ${result.currentPage} of ${result.totalPages}`);
for (const code of result.items ?? []) {
console.log(`${code.code} - ${code.description}`);
}List acquisition purposes (finalidade de aquisição).
List issuer tax profiles (perfil fiscal do emissor).
List recipient tax profiles (perfil fiscal do destinatário).
All methods accept:
| Parameter | Type | Required | Description |
|---|---|---|---|
options.pageIndex |
number |
No | Page index, 1-based (default: 1) |
options.pageCount |
number |
No | Items per page (default: 50) |
Returns: TaxCodePaginatedResponse — Paginated list of tax codes.
interface TaxCode {
code?: string;
description?: string;
}
interface TaxCodePaginatedResponse {
items?: TaxCode[];
currentPage?: number;
totalPages?: number;
totalCount?: number;
}
interface TaxCodeListOptions {
pageIndex?: number;
pageCount?: number;
}See src/core/types.ts for the complete type definitions.
Resource: nfe.productInvoices
API Host: api.nfse.io
Authentication: Uses dataApiKey (falls back to apiKey)
Full lifecycle management for NF-e (Nota Fiscal Eletrônica de Produto) product invoices — issue, list, retrieve, cancel, send correction letters (CC-e), disable invoice numbers, and download files (PDF/XML).
Important: Issue, cancel, correction letter, and disablement operations are asynchronous — they return 202/204 indicating the request was enqueued. Completion is notified via webhooks.
Issue a product invoice (NF-e) by posting it to the processing queue.
const result = await nfe.productInvoices.create('company-id', {
operationNature: 'Venda de mercadoria',
operationType: 'Outgoing',
buyer: { name: 'Empresa LTDA', federalTaxNumber: 12345678000190 },
items: [{ code: 'PROD-001', description: 'Produto X', quantity: 1, unitAmount: 100 }],
payment: [{ paymentDetail: [{ method: 'Cash', amount: 100 }] }],
});Issue an NF-e specifying a particular state tax registration (Inscrição Estadual).
const result = await nfe.productInvoices.createWithStateTax('company-id', 'state-tax-id', invoiceData);List product invoices with cursor-based pagination. The environment option is required.
const invoices = await nfe.productInvoices.list('company-id', {
environment: 'Production', // Required: 'Production' or 'Test'
limit: 10, // Optional (default: 10)
startingAfter: 'cursor-id', // Optional: cursor-based pagination
q: "buyer.name:'EMPRESA'", // Optional: ElasticSearch query
});
for (const inv of invoices.productInvoices ?? []) {
console.log(inv.id, inv.status);
}Retrieve full details of a single NF-e invoice.
const invoice = await nfe.productInvoices.retrieve('company-id', 'invoice-id');
console.log(invoice.status, invoice.authorization?.accessKey);Cancel a product invoice (asynchronous — enqueues for cancellation).
const result = await nfe.productInvoices.cancel('company-id', 'invoice-id', 'Erro de digitação');List items (products/services) for a specific invoice.
const items = await nfe.productInvoices.listItems('company-id', 'invoice-id', { limit: 20 });List fiscal events for a specific invoice.
const events = await nfe.productInvoices.listEvents('company-id', 'invoice-id');Get the URL for the DANFE PDF file.
const pdf = await nfe.productInvoices.downloadPdf('company-id', 'invoice-id');
console.log('PDF URL:', pdf.uri);
// Force regeneration
const pdfForced = await nfe.productInvoices.downloadPdf('company-id', 'invoice-id', true);Get the URL for the authorized NF-e XML file.
const xml = await nfe.productInvoices.downloadXml('company-id', 'invoice-id');
console.log('XML URL:', xml.uri);Get the URL for the NF-e rejection XML (uses /xml-rejection canonical path).
Get the URL for the contingency authorization (EPEC) XML.
Send a correction letter (Carta de Correção — CC-e). The reason must be 15–1,000 characters.
const result = await nfe.productInvoices.sendCorrectionLetter(
'company-id',
'invoice-id',
'Correcao do endereco do destinatario conforme novo cadastro'
);Get the URL for the CC-e DANFE PDF.
Get the URL for the CC-e XML.
Disable (inutilizar) a specific product invoice by ID.
await nfe.productInvoices.disable('company-id', 'invoice-id', 'Numeração inutilizada');Disable a range of invoice numbers.
const result = await nfe.productInvoices.disableRange('company-id', {
environment: 'Production',
serie: 1,
state: 'SP',
beginNumber: 100,
lastNumber: 110,
reason: 'Faixa de numeração inutilizada',
});Parameters common to sub-list methods (listItems, listEvents):
| Parameter | Type | Required | Description |
|---|---|---|---|
options.limit |
number |
No | Items per page (default: 10) |
options.startingAfter |
number |
No | Cursor for pagination |
Resource: nfe.stateTaxes
API Host: api.nfse.io
Authentication: Uses dataApiKey (falls back to apiKey)
CRUD operations for company state tax registrations (Inscrições Estaduais). State taxes define the series, numbering, environment, and state code configuration required for NF-e issuance.
List all state tax registrations for a company.
const result = await nfe.stateTaxes.list('company-id');
for (const tax of result.stateTaxes ?? []) {
console.log(tax.id, tax.taxNumber, tax.code, tax.serie, tax.status);
}Create a new state tax registration. Body is automatically wrapped as { stateTax: data }.
const tax = await nfe.stateTaxes.create('company-id', {
taxNumber: '123456789',
serie: 1,
number: 1,
code: 'sP',
environmentType: 'production',
type: 'nFe',
});
console.log('Created:', tax.id);Retrieve a specific state tax registration by ID.
const tax = await nfe.stateTaxes.retrieve('company-id', 'state-tax-id');Update an existing state tax registration. Body is automatically wrapped as { stateTax: data }.
const updated = await nfe.stateTaxes.update('company-id', 'state-tax-id', {
serie: 2,
environmentType: 'test',
});Delete a state tax registration.
await nfe.stateTaxes.delete('company-id', 'state-tax-id');type NfeStateTaxType = 'default' | 'nFe' | 'nFCe';
type NfeStateTaxEnvironmentType = 'none' | 'production' | 'test';
type NfeStateTaxStatus = 'inactive' | 'none' | 'active';
interface NfeStateTax {
id?: string;
companyId?: string;
accountId?: string;
code?: NfeStateTaxStateCode;
environmentType?: NfeStateTaxEnvironmentType;
taxNumber?: string;
serie?: number;
number?: number;
status?: NfeStateTaxStatus;
specialTaxRegime?: string;
securityCredential?: NfeStateTaxSecurityCredential;
type?: NfeStateTaxType;
series?: NfeStateTaxSeries[];
batchId?: string;
createdOn?: string;
modifiedOn?: string;
}See src/core/types.ts for the complete type definitions.
interface NfeConfig {
apiKey?: string;
dataApiKey?: string; // API key for data/query services (Addresses, CT-e, CNPJ, CPF)
environment?: 'production' | 'development';
baseUrl?: string;
timeout?: number;
retryConfig?: RetryConfig;
}
interface RetryConfig {
maxRetries?: number;
baseDelay?: number;
maxDelay?: number;
retryableStatuses?: number[];
}
interface PaginationOptions {
pageCount?: number;
pageIndex?: number;
}
interface PollOptions {
maxAttempts?: number;
intervalMs?: number;
}
interface ListResponse<T> {
items: T[];
totalCount: number;
pageIndex: number;
pageCount: number;
}interface Company {
id: string;
name: string;
federalTaxNumber: string;
email: string;
address: Address;
// ... other fields
}
interface ServiceInvoice {
id: string;
number?: string;
status: ServiceInvoiceStatus;
borrower: ServiceInvoiceBorrower;
cityServiceCode: string;
servicesAmount: number;
// ... other fields
}
interface LegalPerson {
id: string;
name: string;
federalTaxNumber: string; // CNPJ
email?: string;
// ... other fields
}
interface NaturalPerson {
id: string;
name: string;
federalTaxNumber: string; // CPF
email?: string;
// ... other fields
}
interface Webhook {
id: string;
url: string;
events: string[];
secret?: string;
active: boolean;
// ... other fields
}
interface Address {
country: string;
postalCode: string;
street: string;
number: string;
additionalInformation?: string;
district?: string;
city: City;
state: string;
}
interface City {
code: string;
name: string;
}
type ServiceInvoiceStatus =
| 'pending'
| 'issued'
| 'cancelled'
| 'error';The SDK uses a comprehensive error hierarchy:
import {
NfeError,
AuthenticationError,
ValidationError,
NotFoundError,
RateLimitError,
ServerError,
ConnectionError,
TimeoutError,
PollingTimeoutError,
isNfeError,
isAuthenticationError
} from 'nfe-io';| Error Class | HTTP Status | Description |
|---|---|---|
AuthenticationError |
401 | Invalid API key |
ValidationError |
400, 422 | Request validation failed |
NotFoundError |
404 | Resource not found |
ConflictError |
409 | Resource conflict |
RateLimitError |
429 | Rate limit exceeded |
ServerError |
500, 502, 503 | Server error |
ConnectionError |
- | Network/connection failure |
TimeoutError |
408 | Request timeout |
PollingTimeoutError |
- | Polling exceeded max attempts |
import {
AuthenticationError,
ValidationError,
NotFoundError,
isNfeError
} from 'nfe-io';
try {
const invoice = await nfe.serviceInvoices.create(companyId, data);
} catch (error) {
if (error instanceof AuthenticationError) {
console.error('Invalid API key');
} else if (error instanceof ValidationError) {
console.error('Validation errors:', error.details);
} else if (error instanceof NotFoundError) {
console.error('Company not found');
} else if (isNfeError(error)) {
console.error('NFE.io error:', error.message);
} else {
console.error('Unexpected error:', error);
}
}All NFE.io errors extend NfeError and include:
class NfeError extends Error {
type: ErrorType;
statusCode?: number;
details?: any;
requestId?: string;
}const nfe = new NfeClient({
apiKey: 'your-api-key',
retryConfig: {
maxRetries: 5,
baseDelay: 2000,
maxDelay: 60000,
retryableStatuses: [408, 429, 500, 502, 503, 504]
}
});NFE.io uses async processing for invoices (202 responses). The SDK provides two approaches:
Manual Polling:
const result = await nfe.serviceInvoices.create(companyId, data);
if (result.status === 'pending') {
const invoice = await nfe.pollUntilComplete(result.location, {
maxAttempts: 60,
intervalMs: 3000
});
console.log('Invoice issued:', invoice.number);
} else {
console.log('Invoice issued immediately:', result.number);
}Automatic Polling (Recommended):
const invoice = await nfe.serviceInvoices.createAndWait(companyId, data, {
maxAttempts: 30,
interval: 2000
});
console.log('Invoice issued:', invoice.number);import { isEnvironmentSupported, getRuntimeInfo } from 'nfe-io';
// Check environment compatibility
const support = isEnvironmentSupported();
if (!support.supported) {
console.error('Environment issues:', support.issues);
}
// Get runtime information
const info = getRuntimeInfo();
console.log('SDK Version:', info.sdkVersion);
console.log('Node Version:', info.nodeVersion);
console.log('Platform:', info.platform);import { createClientFromEnv, validateApiKeyFormat } from 'nfe-io';
// Create client from environment variable
// Requires NFE_API_KEY environment variable
const nfe = createClientFromEnv('production');
// Validate API key format
const validation = validateApiKeyFormat('my-api-key');
if (!validation.valid) {
console.error('API key issues:', validation.issues);
}The SDK is fully typed with TypeScript:
import type {
NfeConfig,
ServiceInvoice,
ServiceInvoiceData,
Company,
LegalPerson,
NaturalPerson,
Webhook,
ListResponse,
PaginationOptions,
InboundInvoiceMetadata,
InboundProductInvoiceMetadata,
InboundSettings,
EnableInboundOptions,
ManifestEventType
} from 'nfe-io';
const config: NfeConfig = {
apiKey: 'your-api-key',
environment: 'production'
};
const invoice: ServiceInvoice = await nfe.serviceInvoices.retrieve(
'company-id',
'invoice-id'
);The SDK is designed to be extensible. See CONTRIBUTING.md for guidance on:
- Creating MCP (Model Context Protocol) integrations
- Building n8n workflow nodes
- Developing custom adapters
- Extending the HTTP client
import { HttpClient } from 'nfe-io/core/http/client';
class CustomResource {
constructor(private http: HttpClient) {}
async customMethod(id: string): Promise<any> {
return this.http.get(`/custom/${id}`);
}
}
// Extend NfeClient
import { NfeClient } from 'nfe-io';
class ExtendedNfeClient extends NfeClient {
public readonly custom: CustomResource;
constructor(config: NfeConfig) {
super(config);
this.custom = new CustomResource(this.http);
}
}- Documentation: https://nfe.io/docs
- Repository: https://github.com/nfe/client-nodejs
- Issues: https://github.com/nfe/client-nodejs/issues
MIT License - see LICENSE for details