SDK PHP oficial da API NFE.io. PHP 8.2+ moderno, zero dependências em runtime, projetado em paridade com o SDK Node.js.
| Linha | Constraint Composer (nfe/nfe) |
Situação |
|---|---|---|
| v3 | ^3.0 (atualmente v3.0.0) |
✅ Estável (GA) — desenvolvimento na branch canônica master |
| v2 | ^2.0 (congelada em 2.5) |
❄️ Congelada — preservada nas branches v2 / v2-legacy, sem novas atualizações |
A v2 e a v3 compartilham o mesmo slug Packagist (nfe/nfe). Composer resolve cada constraint para a major correta automaticamente.
- PHP 8.2, 8.3 ou 8.4
- Extensões:
ext-curl,ext-json,ext-mbstring
# v3 estável (atual)
composer require nfe/nfe:^3.0
# v2 legada (congelada)
composer require nfe/nfe:^2.0Além do pacote de código, este repositório publica uma skill de agente (nfeio-php-sdk)
que ensina assistentes de IA (Claude Code, Cursor, Copilot, etc.) a usar o SDK corretamente.
São dois canais distintos:
| Canal | Comando | O quê |
|---|---|---|
| Código (Composer / Packagist) | composer require nfe/nfe |
O SDK PHP |
| Skill de agente (skills.sh) | npx skills add https://github.com/nfe/client-php --skill nfeio-php-sdk |
O guia de uso para agentes |
O atalho npx skills add nfe/client-php também funciona. A skill é lida da árvore do
GitHub (slug nfe/client-php); ela não é baixada pelo composer require (fica fora
do dist via .gitattributes export-ignore).
use Nfe\Client;
use Nfe\Environment;
$nfe = new Client(
apiKey: $_ENV['NFE_API_KEY'],
environment: Environment::Production,
);
// Emite uma nota de serviço (NFS-e)
$result = $nfe->serviceInvoices->create($companyId, [
'borrower' => [
'federalTaxNumber' => 12345678901234,
'name' => 'Cliente exemplo',
'email' => 'cliente@example.com',
],
'cityServiceCode' => '01234',
'federalServiceCode' => '01.02',
'description' => 'Serviço prestado',
'servicesAmount' => 1000.00,
]);
if ($result instanceof Nfe\Response\Pending) {
// 202 — nota está sendo processada de forma assíncrona
echo "Pendente — invoiceId: {$result->invoiceId()}\n";
} else {
// 201 — nota emitida imediatamente
echo "Emitida: {$result->resource()->id}\n";
}A plataforma NFE.io separa o faturamento entre a API principal (emissão, empresas, webhooks — cobrada por documento) e a API de serviços de dados (consultas de CEP/CNPJ/CPF, query de NF-e/NFC-e — cobrada por consulta, tipicamente um plano separado). Alguns integradores possuem uma única chave com ambos os planos; outros possuem duas chaves distintas.
O SDK aceita as duas. Passe dataApiKey quando você tiver uma chave
dedicada para serviços de dados; o SDK roteia addresses,
legalEntityLookup, naturalPersonLookup, productInvoiceQuery e
consumerInvoiceQuery para ela. Quando dataApiKey é omitido, essas
chamadas caem por padrão na apiKey — mesma cadeia do resolveDataApiKey()
do SDK Node.
$nfe = new Client(
apiKey: $_ENV['NFE_API_KEY'],
dataApiKey: $_ENV['NFE_DATA_API_KEY'] ?? null,
);
// Roteado via apiKey (API principal)
$nfe->serviceInvoices->retrieve($companyId, $invoiceId);
// Roteado via dataApiKey quando definida; senão, via apiKey
$nfe->addresses->lookupByPostalCode('01310-100');
$nfe->legalEntityLookup->getBasicInfo('12.345.678/0001-90');Se você ver
Nfe\Exception\AuthorizationException(HTTP 403) em chamadas de consulta, a causa mais provável é que a chave em uso não possui o plano de serviços de dados. Provisione umadataApiKeycom o plano de dados e o SDK fará o roteamento automaticamente.
| Propriedade | Família de endpoints |
|---|---|
$nfe->serviceInvoices |
NFS-e (nota de serviço) |
$nfe->productInvoices |
NF-e (nota de produto) |
$nfe->consumerInvoices |
NFC-e (nota ao consumidor) — emissão + consulta |
$nfe->transportationInvoices |
CT-e |
$nfe->inboundProductInvoices |
NF-e de entrada |
$nfe->productInvoiceQuery |
Consulta de NF-e |
$nfe->consumerInvoiceQuery |
Consulta de NFC-e |
$nfe->companies |
Gestão de empresas |
$nfe->legalPeople |
Pessoa jurídica (PJ) |
$nfe->naturalPeople |
Pessoa física (PF) |
$nfe->webhooks |
Configuração de webhooks |
$nfe->addresses |
Consulta de CEP |
$nfe->legalEntityLookup |
Consulta de CNPJ |
$nfe->naturalPersonLookup |
Consulta de CPF |
$nfe->taxCalculation |
Cálculo de impostos |
$nfe->taxCodes |
Códigos fiscais (NBS / CNAE) |
$nfe->stateTaxes |
Inscrição estadual |
Os blocos abaixo cobrem os usos mais comuns de cada família. Os tipos de retorno declarados em cada método são a fonte da verdade — IDEs com PHP 8.2+ resolvem tudo via type hints.
Notas de Serviço (NFS-e) — $nfe->serviceInvoices
// Emitir nota (assíncrono — retorna Pending|Issued)
$result = $nfe->serviceInvoices->create($companyId, [
'borrower' => [
'federalTaxNumber' => 12345678901,
'name' => 'João da Silva',
'email' => 'joao@example.com',
],
'cityServiceCode' => '10677',
'description' => 'Consultoria',
'servicesAmount' => 1500.00,
]);
// Listar com filtros e paginação (pageIndex é 1-based)
$lista = $nfe->serviceInvoices->list($companyId, [
'pageCount' => 50,
'pageIndex' => 1,
'issuedBegin' => '2026-01-01',
'issuedEnd' => '2026-01-31',
]);
foreach ($lista->data as $nota) {
echo "{$nota->id} → {$nota->flowStatus}\n";
}
// Consultar, status, cancelar, reenviar por email
$nota = $nfe->serviceInvoices->retrieve($companyId, $invoiceId);
$status = $nfe->serviceInvoices->getStatus($companyId, $invoiceId);
$nfe->serviceInvoices->cancel($companyId, $invoiceId);
$nfe->serviceInvoices->sendEmail($companyId, $invoiceId); // envia para o email do tomador
// Baixar PDF e XML (retornam bytes brutos)
file_put_contents('nota.pdf', $nfe->serviceInvoices->downloadPdf($companyId, $invoiceId));
file_put_contents('nota.xml', $nfe->serviceInvoices->downloadXml($companyId, $invoiceId));Notas de Produto (NF-e) — $nfe->productInvoices
Ciclo completo: emissão, listagem, consulta, cancelamento, carta de correção (CC-e) e inutilização de faixa.
// Emitir NF-e (assíncrono)
$result = $nfe->productInvoices->create($companyId, [
'operationNature' => 'Venda de mercadoria',
'operationType' => 'Outgoing',
'buyer' => ['name' => 'Empresa LTDA', 'federalTaxNumber' => 12345678000190],
'items' => [[
'code' => 'PROD-001',
'description' => 'Produto X',
'quantity' => 1,
'unitAmount' => 100.00,
]],
]);
// Listar (environment obrigatório: Production | Development)
$lista = $nfe->productInvoices->list($companyId, [
'environment' => 'Production',
'pageCount' => 10,
]);
// Carta de correção (CC-e) — razão entre 15 e 1000 caracteres
$nfe->productInvoices->sendCorrectionLetter(
$companyId,
$invoiceId,
'Correcao do endereco do destinatario conforme novo cadastro',
);
// Inutilizar faixa de numeração
$nfe->productInvoices->disableRange($companyId, [
'environment' => 'Production',
'serie' => 1,
'state' => 'SP',
'beginNumber' => 100,
'lastNumber' => 110,
]);
// Downloads
$pdf = $nfe->productInvoices->downloadPdf($companyId, $invoiceId);
$xml = $nfe->productInvoices->downloadXml($companyId, $invoiceId);
$ccePdf = $nfe->productInvoices->downloadCorrectionLetterPdf($companyId, $invoiceId);Emissão, cancelamento, CC-e e inutilização são assíncronos (HTTP 202/204). A conclusão chega via webhook.
Notas ao Consumidor (NFC-e) — $nfe->consumerInvoices
NFC-e segue o mesmo padrão de NF-e (emissão assíncrona, downloads, cancelamento, inutilização).
$result = $nfe->consumerInvoices->create($companyId, $payload);
$lista = $nfe->consumerInvoices->list($companyId, ['environment' => 'Production']);
$nota = $nfe->consumerInvoices->retrieve($companyId, $invoiceId);
$nfe->consumerInvoices->cancel($companyId, $invoiceId);
// Downloads (retornam bytes)
file_put_contents('nfce.pdf', $nfe->consumerInvoices->downloadPdf($companyId, $invoiceId));
file_put_contents('nfce.xml', $nfe->consumerInvoices->downloadXml($companyId, $invoiceId));
// Inutilizar faixa
$nfe->consumerInvoices->disableRange($companyId, [
'environment' => 'Production',
'serie' => 1,
'state' => 'SP',
'beginNumber' => 1000,
'lastNumber' => 1010,
]);CT-e — $nfe->transportationInvoices
Consulta de CT-e via Distribuição DFe. Requer certificado A1 válido na empresa.
// Ativar busca automática
$settings = $nfe->transportationInvoices->enable($companyId, [
'startFromNsu' => 12345, // opcional
]);
// Verificar configuração atual / desativar
$config = $nfe->transportationInvoices->getSettings($companyId);
$nfe->transportationInvoices->disable($companyId);
// Consultar CT-e por chave (44 dígitos)
$accessKey = '35240112345678000190570010000001231234567890';
$cte = $nfe->transportationInvoices->retrieve($companyId, $accessKey);
echo "Remetente: {$cte->nameSender}, valor: {$cte->totalInvoiceAmount}";
// Baixar XML
file_put_contents('cte.xml',
$nfe->transportationInvoices->downloadXml($companyId, $accessKey),
);
// Evento + XML do evento
$evento = $nfe->transportationInvoices->getEvent($companyId, $accessKey, $eventKey);
$eventoXml = $nfe->transportationInvoices->downloadEventXml($companyId, $accessKey, $eventKey);NF-e de Entrada (Distribuição) — $nfe->inboundProductInvoices
Recebe NF-e emitidas contra a empresa via Distribuição DFe.
// Ativar busca automática
$nfe->inboundProductInvoices->enableAutoFetch($companyId, [
'environmentSEFAZ' => 'Production',
'webhookVersion' => '2',
]);
// Consultar NF-e por chave (formato webhook v2 — recomendado)
$accessKey = '35240112345678000190550010000001231234567890';
$nota = $nfe->inboundProductInvoices->getProductInvoiceDetails($companyId, $accessKey);
echo "Emissor: {$nota['issuer']['name']}";
// Baixar XML, PDF (DANFE) e JSON
file_put_contents('nfe.xml', $nfe->inboundProductInvoices->getXml($companyId, $accessKey));
file_put_contents('nfe.pdf', $nfe->inboundProductInvoices->getPdf($companyId, $accessKey));
$json = $nfe->inboundProductInvoices->getJson($companyId, $accessKey);
// Manifestação (Ciência da Operação por padrão = 210210)
$nfe->inboundProductInvoices->manifest($companyId, $accessKey);
$nfe->inboundProductInvoices->manifest($companyId, $accessKey, 210220); // Confirmação da Operação
// Reprocessar um webhook entregue
$nfe->inboundProductInvoices->reprocessWebhook($companyId, $accessKey);| Código | Evento de manifestação |
|---|---|
210210 |
Ciência da Operação (padrão) |
210220 |
Confirmação da Operação |
210240 |
Operação não Realizada |
Consulta de NF-e na SEFAZ — $nfe->productInvoiceQuery
Consulta NF-e diretamente na SEFAZ pela chave de acesso. Read-only, sem escopo de empresa. Usa dataApiKey quando configurada.
$accessKey = '35240112345678000190550010000001231234567890';
$nota = $nfe->productInvoiceQuery->retrieve($accessKey);
$pdf = $nfe->productInvoiceQuery->downloadPdf($accessKey);
$xml = $nfe->productInvoiceQuery->downloadXml($accessKey);
$eventos = $nfe->productInvoiceQuery->listEvents($accessKey);Consulta de CFe-SAT / NFC-e na SEFAZ — $nfe->consumerInvoiceQuery
$accessKey = '35240112345678000190590000000012341234567890';
$cupom = $nfe->consumerInvoiceQuery->retrieve($accessKey);
file_put_contents('cfe.xml', $nfe->consumerInvoiceQuery->downloadXml($accessKey));Empresas — $nfe->companies
// CRUD
$empresa = $nfe->companies->create([
'federalTaxNumber' => 12345678000190,
'name' => 'Minha Empresa LTDA',
'email' => 'empresa@example.com',
// ... endereço, regime tributário, etc.
]);
$lista = $nfe->companies->list();
$todas = $nfe->companies->listAll(); // sem paginação — itera tudo
$empresa = $nfe->companies->retrieve($companyId);
$atualizada = $nfe->companies->update($companyId, ['email' => 'novo@example.com']);
$nfe->companies->remove($companyId);
// Buscas
$empresa = $nfe->companies->findByTaxNumber('12345678000190');
$matches = $nfe->companies->findByName('Minha Empresa');
// Certificado A1
$status = $nfe->companies->getCertificateStatus($companyId);
if ($status->hasCertificate && $status->isExpiringSoon) {
echo "Expira em {$status->daysUntilExpiration} dia(s)\n";
}
// Painel de certificados (filtros prontos)
$comCert = $nfe->companies->getCompaniesWithCertificates();
$expirando = $nfe->companies->getCompaniesWithExpiringCertificates(thresholdDays: 30);Pessoas (PJ e PF) — $nfe->legalPeople / $nfe->naturalPeople
Ambos os recursos têm a mesma forma. Exemplo com PJ:
$pj = $nfe->legalPeople->create($companyId, [
'federalTaxNumber' => '12345678000190',
'name' => 'Cliente PJ',
'email' => 'pj@example.com',
]);
$lista = $nfe->legalPeople->list($companyId);
$pj = $nfe->legalPeople->retrieve($companyId, $legalPersonId);
$atualizada = $nfe->legalPeople->update($companyId, $legalPersonId, ['email' => 'novo@x.com']);
$nfe->legalPeople->delete($companyId, $legalPersonId);
// Buscar por documento
$pj = $nfe->legalPeople->findByTaxNumber($companyId, '12345678000190');
// Criação em lote
$nfe->legalPeople->createBatch($companyId, [
['federalTaxNumber' => '11111111000111', 'name' => 'PJ 1', 'email' => 'a@x.com'],
['federalTaxNumber' => '22222222000122', 'name' => 'PJ 2', 'email' => 'b@x.com'],
]);Para pessoas físicas ($nfe->naturalPeople), troque federalTaxNumber (CPF de 11 dígitos) e use os mesmos métodos.
Inscrições Estaduais — $nfe->stateTaxes
Necessário para emitir NF-e de produto.
$lista = $nfe->stateTaxes->list($companyId);
$ie = $nfe->stateTaxes->create($companyId, [
'taxNumber' => '123456789',
'serie' => 1,
'number' => 1,
'code' => 'SP',
'environmentType' => 'production',
'type' => 'nFe',
]);
$ie = $nfe->stateTaxes->retrieve($companyId, $stateTaxId);
$nfe->stateTaxes->update($companyId, $stateTaxId, ['serie' => 2]);
$nfe->stateTaxes->delete($companyId, $stateTaxId);Consulta de CEP — $nfe->addresses
// Por CEP (8 dígitos, com ou sem hífen). O host address.api.nfe.io/v2
// suporta apenas consulta por CEP.
$resultado = $nfe->addresses->lookupByPostalCode('01310-100');
foreach ($resultado->addresses as $end) {
echo "{$end['street']}, {$end['city']['name']}/{$end['state']}\n";
}Consulta de CNPJ — $nfe->legalEntityLookup
// Dados cadastrais (Receita Federal)
$result = $nfe->legalEntityLookup->getBasicInfo('12.345.678/0001-90');
$pj = $result->legalEntity;
echo "Razão Social: {$pj['name']}, Status: {$pj['status']}";
// Com opções (atualizar endereço/código IBGE via Correios)
$result = $nfe->legalEntityLookup->getBasicInfo('12345678000190', [
'updateAddress' => false,
'updateCityCode' => true,
]);
// IE por estado
$ieSP = $nfe->legalEntityLookup->getStateTaxInfo('SP', '12345678000190');
// Avaliar IE para emissão de nota
$avaliacao = $nfe->legalEntityLookup->getStateTaxForInvoice('MG', '12345678000190');
// Melhor IE sugerida
$sugestao = $nfe->legalEntityLookup->getSuggestedStateTaxForInvoice('SP', '12345678000190');Consulta de CPF — $nfe->naturalPersonLookup
// CPF + data de nascimento (string YYYY-MM-DD ou DateTimeImmutable)
$result = $nfe->naturalPersonLookup->getStatus('123.456.789-01', '1990-01-15');
echo "Nome: {$result->name}, Situação: {$result->status}";
// DateTimeImmutable também funciona
$result = $nfe->naturalPersonLookup->getStatus(
'12345678901',
new DateTimeImmutable('1990-01-15'),
);Cálculo de Impostos — $nfe->taxCalculation
Engine de cálculo ICMS / ICMS-ST / PIS / COFINS / IPI / II.
$resultado = $nfe->taxCalculation->calculate($tenantId, [
'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,
]],
]);
foreach ($resultado['items'] ?? [] as $item) {
echo "Item {$item['id']}: CFOP={$item['cfop']}\n";
echo " ICMS: CST={$item['icms']['cst']}, valor={$item['icms']['vICMS']}\n";
}Códigos auxiliares — $nfe->taxCodes
$operacoes = $nfe->taxCodes->listOperationCodes(['pageIndex' => 1, 'pageCount' => 20]);
$finalidades = $nfe->taxCodes->listAcquisitionPurposes();
$perfisEmissor = $nfe->taxCodes->listIssuerTaxProfiles();
$perfisDestinatario = $nfe->taxCodes->listRecipientTaxProfiles();
foreach ($operacoes->items as $cod) {
echo "{$cod['code']} — {$cod['description']}\n";
}$webhook = $nfe->webhooks->create($companyId, [
'url' => 'https://meuapp.com.br/api/webhooks/nfe',
'events' => ['invoice.issued', 'invoice.cancelled', 'invoice.error'],
'active' => true,
]);
// Listar / atualizar / remover / testar
$lista = $nfe->webhooks->list($companyId);
$nfe->webhooks->update($companyId, $webhookId, ['events' => ['invoice.issued']]);
$nfe->webhooks->delete($companyId, $webhookId);
$nfe->webhooks->test($companyId, $webhookId);
// Listar eventos suportados pela API
$eventos = $nfe->webhooks->getAvailableEvents();O SDK fornece um helper estático alinhado ao esquema canônico usado pela NFE.io (HMAC-SHA1 sobre X-Hub-Signature):
use Nfe\Webhook;
use Nfe\Exception\SignatureVerificationException;
try {
$event = Webhook::constructEvent(
payload: file_get_contents('php://input'),
sigHeader: $_SERVER['HTTP_X_HUB_SIGNATURE'] ?? '',
secret: $_ENV['NFE_WEBHOOK_SECRET'],
);
// $event é um WebhookEvent tipado: $event->type, $event->data, $event->id, $event->createdAt
} catch (SignatureVerificationException $e) {
http_response_code(403);
exit;
}
// Roteamento por tipo de evento
match ($event->type) {
'invoice.issued' => emitidaHandler($event->data),
'invoice.cancelled' => canceladaHandler($event->data),
'invoice.error' => erroHandler($event->data),
default => null,
};
http_response_code(200);Para emissão assíncrona de notas (HTTP 202), a v3.0 retorna uma resposta discriminada Pending | Issued. Um helper pollUntilComplete() chegará em uma release 3.x posterior; até lá, faça o loop manualmente em um worker/CLI:
use Nfe\Util\FlowStatus;
$result = $nfe->serviceInvoices->create($companyId, $data);
if ($result instanceof Nfe\Response\Pending) {
$invoiceId = $result->invoiceId();
do {
sleep(2);
$invoice = $nfe->serviceInvoices->retrieve($companyId, $invoiceId);
} while (!FlowStatus::isTerminal($invoice->flowStatus));
}FlowStatus::TERMINAL lista os quatro estados terminais (Issued, IssueFailed, Cancelled, CancelFailed). Espelha TERMINAL_FLOW_STATES do SDK Node.
Toda resposta não-2xx é mapeada para uma exceção tipada que estende Nfe\Exception\ApiErrorException. Capture a classe base para um handler genérico, ou a subclasse para uma recuperação direcionada:
| HTTP | Exceção | Causa típica |
|---|---|---|
| 400 | InvalidRequestException |
Payload mal formado, falha de validação |
| 401 | AuthenticationException |
Chave de API ausente ou inválida |
| 403 | AuthorizationException |
Chave válida, mas o plano/escopo recusa a ação (ex.: requer chave de serviços de dados) |
| 404 | NotFoundException |
Recurso não existe |
| 429 | RateLimitException |
Throttling — consulte Retry-After |
| 5xx | ServerException |
Falha na infraestrutura upstream / NFE.io |
| — | ApiConnectionException |
Falha de rede, DNS, TLS, timeout |
| — | SignatureVerificationException |
Assinatura do payload do webhook não confere |
use Nfe\Exception\ApiErrorException;
use Nfe\Exception\AuthorizationException;
use Nfe\Exception\RateLimitException;
try {
$nfe->addresses->lookupByPostalCode($cep);
} catch (AuthorizationException $e) {
// 403 — chave provavelmente sem o plano de serviços de dados
error_log("Consulta negada: {$e->getMessage()}");
} catch (RateLimitException $e) {
// Consulte $e->responseHeaders['retry-after']
throw $e;
} catch (ApiErrorException $e) {
// Qualquer outra resposta não-2xx
error_log("Erro na API {$e->statusCode}: {$e->getMessage()}");
}Cada exceção expõe $statusCode, $responseBody, $responseHeaders e $errorCode para diagnóstico.
Quando você precisa de mais controle (timeout, retry, transporte customizado, logger), construa um Config explícito:
use Nfe\Client;
use Nfe\Config;
use Nfe\Environment;
use Nfe\Http\RetryPolicy;
use Psr\Log\LoggerInterface;
$config = new Config(
apiKey: $_ENV['NFE_API_KEY'],
dataApiKey: $_ENV['NFE_DATA_API_KEY'] ?? null,
environment: Environment::Production,
timeout: 60, // segundos
retry: new RetryPolicy(
maxRetries: 3,
baseDelay: 1.0, // segundos
maxDelay: 30.0, // segundos
jitter: 0.3, // ±30%
),
logger: $myPsr3Logger, // opcional, qualquer LoggerInterface
transport: $myCustomTransport, // opcional, ex.: adaptador PSR-18
userAgentSuffix: 'MeuApp/1.2.3', // sufixo identificador no User-Agent
);
$nfe = new Client(config: $config);| Campo | Padrão | Descrição |
|---|---|---|
apiKey |
obrigatório | Chave principal (emissão, empresas, webhooks). |
dataApiKey |
null |
Chave separada para serviços de dados. Quando null, faz fallback para apiKey. |
environment |
Production |
Production ou Sandbox. |
timeout |
60 |
Timeout HTTP por requisição (segundos). |
retry |
new RetryPolicy() |
Backoff exponencial com jitter simétrico. Use RetryPolicy::none() para desabilitar. |
transport |
CurlTransport |
Implementação de Nfe\Http\Transport (ex.: adaptador PSR-18). |
logger |
null |
Qualquer Psr\Log\LoggerInterface. PSR-3 não é dependência em runtime. |
userAgentSuffix |
null |
Identificador do integrador (ex.: WHMCS/8.10). |
RequestOptions sobrescreve apiKey, baseUrl e timeout em uma chamada específica — útil em integrações multi-tenant:
use Nfe\Http\RequestOptions;
$nota = $nfe->serviceInvoices->retrieve(
$companyId,
$invoiceId,
options: new RequestOptions(apiKey: $chaveDoCliente, timeout: 120),
);O SDK não lê variáveis de ambiente automaticamente — você passa as chaves no construtor. Convenção sugerida (compatível com o SDK Node):
| Variável | Uso |
|---|---|
NFE_API_KEY |
Chave principal. |
NFE_DATA_API_KEY |
Chave separada para CEP/CNPJ/CPF/NF-e query. |
NFE_WEBHOOK_SECRET |
Segredo HMAC do webhook. |
$nfe = new Client(
apiKey: getenv('NFE_API_KEY') ?: throw new RuntimeException('NFE_API_KEY ausente'),
dataApiKey: getenv('NFE_DATA_API_KEY') ?: null,
);Veja MIGRATION.md para o mapeamento completo v2 → v3. Não há retrocompatibilidade — a v3 é uma reescrita limpa.
Veja CONTRIBUTING.md.
MIT — veja LICENSE.