Complete API reference for the NFE.io Node.js SDK v3.
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: 'joao@example.com',
},
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: 'finance@acme.com',
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: 'joao@example.com',
},
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: ['client@example.com'],
});
console.log('✅ Email sent');
// Example 2: Send to multiple recipients
await nfe.serviceInvoices.sendEmail('company-id', 'invoice-id', {
emails: [
'client@example.com',
'finance@example.com',
'accounting@example.com',
],
});
// 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: 'client1@example.com' },
cityServiceCode: '10677',
description: 'Service 1',
servicesAmount: 1000,
},
{
borrower: { federalTaxNumber: 222, name: 'Client 2', email: 'client2@example.com' },
cityServiceCode: '10677',
description: 'Service 2',
servicesAmount: 2000,
},
{
borrower: { federalTaxNumber: 333, name: 'Client 3', email: 'client3@example.com' },
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: 'company@example.com',
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: 'newemail@example.com'
});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: 'legal@example.com'
});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: 'john@example.com'
});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);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
} 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