nfe-io 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 787947713cf27bbdb1f9e22775e2ef2aa59ef185e4015c8ae74e99864c833e2d
4
- data.tar.gz: a32a98bcdbf26b3f4c6a9c143fd46d796d5e5b7e47114309efcb98a0f779f3da
3
+ metadata.gz: 5e9b7b2223a90779e218be5ddb727e1f4c9d707a24767075c7d56132c353347c
4
+ data.tar.gz: 55597041ea75f6c244a5af2e64e0f29ea022b9f5041caf987e6f2bcda6ef3bfa
5
5
  SHA512:
6
- metadata.gz: 495de7c5b904d2de20c731521b8bc608825dbcadb200d86ce98489638a634e57105df3cb17c2b1016c2fb15be59ada705079b624b7343a510dc02f20d6d7dd49
7
- data.tar.gz: 37ab16497cb424cc9fb759c0c824c798cdc06fe5fb5ba35da78d2b7d546507cc194150040011510b5a0c7097672da2e7985b7be27b00ca972a9e998977a75498
6
+ metadata.gz: d0c6297b2146225c8d9e7c228866a836367b484e727a47956ddb59ef084715a8be11f0a0bdbf8cf51d391a763e9736d293ecfb9a89bd21c47e0c56cc8aaeb57e
7
+ data.tar.gz: a6cbb185d13215213bec2387b06c988fd41de2446c2c779424fac01c54b8692fa2de7136c783581ea9160b40f15697d1ea83fad9ab9b06d7ac55a2d22e65c02e
data/CHANGELOG.md CHANGED
@@ -7,6 +7,86 @@ e o projeto adere ao [Versionamento Semântico](https://semver.org/lang/pt-BR/).
7
7
 
8
8
  ## [Não lançado]
9
9
 
10
+ ## [1.1.0] - 2026-07-09
11
+
12
+ > Duas correções de contrato contra a API real: o CRUD de webhooks (provado por
13
+ > sonda ao vivo, 2026-07-02/03, três contas) e a cobertura do retrieve de NFS-e
14
+ > (o DTO descartava mais da metade dos campos). Em ambas, o contrato correto
15
+ > sempre esteve nos specs oficiais (`openapi/nf-servico-v1.yaml` e equivalentes) —
16
+ > o código manuscrito havia divergido deles.
17
+
18
+ ### Corrigido
19
+
20
+ - **O CRUD de webhooks funcionava 0% das vezes**: a rota company-scoped
21
+ `/v1/companies/{id}/webhooks` retorna 404 na API atual. O contrato real é
22
+ account-scoped (`/v2/webhooks`) e exige o request envelopado em
23
+ `{ "webHook": {...} }` (sem ele responde
24
+ `400 "missing required properties: 'webHook'"`), devolvendo a resposta também
25
+ envelopada. Os novos métodos account-scoped envelopam o request
26
+ (create/update) e desembrulham as respostas (create/retrieve/update/list),
27
+ com fallback defensivo para corpo cru.
28
+ - **`Nfe::ServiceInvoice` descartava ~25 dos 44 campos do retrieve de NFS-e**
29
+ (toda a árvore de retenções, `provider`, `taxationType`, `location`,
30
+ `approximateTax`, ...): o `from_api` agora preserva o payload completo em
31
+ `invoice.raw` (padrão do `ConsumerInvoice`), em todas as leituras
32
+ (list/retrieve/cancel/201-issued).
33
+
34
+ ### Adicionado
35
+
36
+ - Métodos account-scoped em `client.webhooks`: `list_account_webhooks`,
37
+ `create_account_webhook`, `retrieve_account_webhook`,
38
+ `update_account_webhook`, `delete_account_webhook`,
39
+ `delete_all_account_webhooks` (destrutivo, nome propositalmente distinto),
40
+ `ping_account_webhook` e `fetch_event_types`.
41
+ - Value object **`Nfe::AccountWebhook`** (`Data.define`, com RBS) com o shape
42
+ real da API: `uri`, `content_type`, `secret` (32–64 caracteres, ecoado no
43
+ create e omitido nas leituras), `filters`, `insecure_ssl`, `headers`,
44
+ `properties`, `status`, `created_on`, `modified_on`. Nota: o spec declara
45
+ `contentType`/`status` como enums inteiros, mas a API serializa strings
46
+ (`"json"`, `"Active"`) — o DTO segue o fio real.
47
+ - `fetch_event_types` retorna os event types reais de
48
+ `GET /v2/webhooks/eventTypes` (46 ids ao vivo, padrão
49
+ `service_invoice.*`/`product_invoice.*`/`consumer_invoice.*`).
50
+ - Teste de alinhamento (RSpec + Psych) amarrando o `Nfe::AccountWebhook` ao
51
+ schema de `/v2/webhooks` em `openapi/nf-servico-v1.yaml` — um sync de spec
52
+ que mude o contrato de webhooks quebra a suíte em vez de driftar.
53
+ - YARD do `create_account_webhook` documenta a verificação de URI na criação
54
+ (a NFE.io faz um ping e exige resposta 2xx) e o `secret` de 32–64 caracteres.
55
+ - YARD do `update_account_webhook` documenta que o `PUT` é substituição
56
+ integral (confirmado ao vivo em 2026-07-03): campos omitidos voltam ao
57
+ padrão — update sem `status` **desativa o webhook**. Envie o objeto completo
58
+ (parta do retrieve).
59
+ - Campos de ISS tipados em `Nfe::ServiceInvoice`: `base_tax_amount`,
60
+ `iss_rate`, `iss_tax_amount`.
61
+ - Value object **`Nfe::ServiceInvoiceBorrower`** (tomador, com RBS):
62
+ `federal_tax_number` sempre `String` (tolerante ao CNPJ alfanumérico da
63
+ IN RFB 2.229/2024, fio Integer ou String) e **ponte Hash** — leituras
64
+ `borrower["..."]`/`borrower.dig(...)` continuam funcionando (delegam ao
65
+ payload cru), agora ao lado dos leitores tipados.
66
+ - Teste de alinhamento (RSpec + Psych) amarrando `Nfe::ServiceInvoice` ao
67
+ schema inline do retrieve em `openapi/nf-servico-v1.yaml`, **ancorado por
68
+ path** (há colisão de `operationId` no spec) — também serve de gatilho de
69
+ migração: quando a resposta for componentizada upstream, o teste falha e
70
+ sinaliza migrar para o modelo gerado.
71
+ - Spec `nf-servico-v1.yaml` atualizado (respostas de erro tipadas com
72
+ `ErrorsResource` em `components.schemas`) + namespace gerado
73
+ `Nfe::Generated::NfServicoV1` (somente o modelo de erros; a resposta de
74
+ sucesso segue inline e o DTO manuscrito).
75
+
76
+ ### Deprecado
77
+
78
+ - Métodos company-scoped de webhooks (`list`, `create`, `retrieve`, `update`,
79
+ `delete`, `test` sobre `/v1/companies/{id}/webhooks`): a rota retorna **404**
80
+ na API atual (confirmado em três contas, 2026-07-02/03). Use os equivalentes
81
+ account-scoped. O comportamento não mudou; remoção fica para a próxima major.
82
+ - `Nfe::WebhookSubscription` (`url`/`events`/`active`) e
83
+ `get_available_events`/`AVAILABLE_EVENTS` (literais `invoice.*`): shapes e
84
+ eventos que a API real rejeita ou desconhece. Use `Nfe::AccountWebhook` e
85
+ `fetch_event_types`.
86
+ - `Nfe::ServiceInvoice#pdf` e `#xml`: campos-fantasma — a resposta do retrieve
87
+ não os traz (sempre `nil`). Use `download_pdf`/`download_xml`. Remoção na
88
+ próxima major.
89
+
10
90
  ## [1.0.0] - 2026-07-02
11
91
 
12
92
  ### Adicionado
@@ -55,6 +135,7 @@ e o projeto adere ao [Versionamento Semântico](https://semver.org/lang/pt-BR/).
55
135
 
56
136
  - Última versão da série `0.x` (legada, baseada em `rest-client`). Congelada, sem manutenção.
57
137
 
58
- [Não lançado]: https://github.com/nfe/client-ruby/compare/v1.0.0...HEAD
138
+ [Não lançado]: https://github.com/nfe/client-ruby/compare/v1.1.0...HEAD
139
+ [1.1.0]: https://github.com/nfe/client-ruby/compare/v1.0.0...v1.1.0
59
140
  [1.0.0]: https://github.com/nfe/client-ruby/compare/v0.3.2...v1.0.0
60
141
  [0.3.2]: https://github.com/nfe/client-ruby/releases/tag/v0.3.2
data/README.md CHANGED
@@ -49,6 +49,21 @@ Ou instalação direta:
49
49
  gem install nfe-io
50
50
  ```
51
51
 
52
+ ## Skill para agentes de IA
53
+
54
+ Além do gem, este repositório publica uma **skill de agente** (`nfeio-ruby-sdk`) que
55
+ ensina assistentes de IA (Claude Code, Cursor, Copilot, etc.) a usar o SDK corretamente.
56
+ São **dois canais distintos**:
57
+
58
+ | Canal | Comando | O quê |
59
+ |---|---|---|
60
+ | Código (RubyGems) | `gem install nfe-io` | O SDK Ruby |
61
+ | Skill de agente ([skills.sh](https://www.skills.sh/)) | `npx skills add https://github.com/nfe/client-ruby --skill nfeio-ruby-sdk` | O guia de uso para agentes |
62
+
63
+ O atalho `npx skills add nfe/client-ruby` também funciona. A skill é lida da árvore do
64
+ GitHub (slug `nfe/client-ruby`); ela **não** vem no `gem install` — o gemspec empacota
65
+ apenas `lib/`, `sig/` e os docs.
66
+
52
67
  ## Quickstart
53
68
 
54
69
  ```ruby
@@ -169,7 +184,7 @@ A `v1` expõe **17 recursos canônicos** no `Nfe::Client` (mais 2 addons RTC).
169
184
  | `companies` | `api.nfe.io` (`/v1`) | Empresas + certificado | `create`, `retrieve`, `list`, `update`, `remove`, `upload_certificate`, `get_certificate_status` |
170
185
  | `legal_people` | `api.nfe.io` (`/v1`) | Pessoas jurídicas (tomadores) | `create`, `retrieve`, `list`, `update`, `delete`, `create_batch`, `find_by_tax_number` |
171
186
  | `natural_people` | `api.nfe.io` (`/v1`) | Pessoas físicas (tomadores) | `create`, `retrieve`, `list`, `update`, `delete`, `create_batch`, `find_by_tax_number` |
172
- | `webhooks` | `api.nfe.io` (`/v1`) | Assinaturas de webhook | `create`, `retrieve`, `list`, `update`, `delete`, `test`, `verify_signature` |
187
+ | `webhooks` | `api.nfe.io` (`/v2`, conta) | Webhooks da conta | `create_account_webhook`, `retrieve_account_webhook`, `list_account_webhooks`, `update_account_webhook`, `delete_account_webhook`, `ping_account_webhook`, `fetch_event_types`, `verify_signature` |
173
188
  | `product_invoices` | `api.nfse.io` (`/v2`) | NF-e | `create`, `create_with_state_tax`, `list`, `retrieve`, `cancel`, `send_correction_letter`, `disable`, `download_*` |
174
189
  | `consumer_invoices` | `api.nfse.io` (`/v2`) | NFC-e | `create`, `create_with_state_tax`, `list`, `retrieve`, `cancel`, `disable_range`, `download_pdf`/`download_xml` |
175
190
  | `transportation_invoices` | `api.nfse.io` (`/v2`) | CT-e (recepção) | `enable`, `disable`, `get_settings`, `retrieve`, `download_xml`, `get_event` |
@@ -267,7 +282,7 @@ discriminado**:
267
282
  `pending?` ⇒ `true`, `issued?` ⇒ `false`.
268
283
  - `*Issued` (HTTP 201, já materializado) — expõe `resource`; `issued?` ⇒ `true`.
269
284
 
270
- Não há `create_and_wait` nem `create_batch` na v1.0 — faça **polling** chamando
285
+ Não há `create_and_wait` nem `create_batch` na v1.x — faça **polling** chamando
271
286
  `retrieve` até um estado terminal, usando `Nfe::FlowStatus.terminal?`. Os estados
272
287
  terminais são: `Issued`, `IssueFailed`, `Cancelled`, `CancelFailed`.
273
288
 
@@ -359,16 +374,26 @@ Devolvem bytes: `service_invoices`, `consumer_invoices`,
359
374
 
360
375
  ## Webhooks
361
376
 
362
- Crie a assinatura e **verifique a assinatura** de cada entrega.
377
+ Crie o webhook (escopo da **conta**, `/v2/webhooks`) e **verifique a
378
+ assinatura** de cada entrega. A NFE.io pinga a `uri` na criação e exige 2xx;
379
+ descubra os filtros válidos com `fetch_event_types`.
363
380
 
364
381
  ```ruby
365
- client.webhooks.create("co_1", {
366
- url: "https://minha-app.com/webhooks/nfe",
367
- events: ["invoice.issued", "invoice.cancelled", "invoice.failed"],
368
- secret: ENV["NFE_WEBHOOK_SECRET"]
369
- })
382
+ client.webhooks.create_account_webhook(
383
+ uri: "https://minha-app.com/webhooks/nfe",
384
+ contentType: "json",
385
+ secret: ENV["NFE_WEBHOOK_SECRET"], # 32–64 caracteres
386
+ filters: ["service_invoice.issued_successfully", "service_invoice.issued_error"],
387
+ status: "Active"
388
+ )
370
389
  ```
371
390
 
391
+ > ⚠️ `update_account_webhook` faz **PUT integral**: campos omitidos voltam ao
392
+ > padrão — um update sem `status` desativa o webhook. Parta do
393
+ > `retrieve_account_webhook` e envie o objeto completo. Os métodos
394
+ > company-scoped (`create`/`list`/... com `company_id`) estão **deprecated** —
395
+ > a rota `/v1/companies/{id}/webhooks` retorna 404 na API atual.
396
+
372
397
  A verificação é **HMAC-SHA1 sobre os bytes crus** da requisição (header
373
398
  `X-Hub-Signature`, comparação case-insensitive e timing-safe). Leia o corpo bruto
374
399
  **antes** de fazer parse do JSON — reserializar (`payload.to_json`) muda
@@ -13,6 +13,7 @@ module Nfe
13
13
  "contribuintes-v2.json" => "sha256:e2d215a19f5dc85c08067d51644e807aae32b6c4754390872670f2e18a938102",
14
14
  "nf-consumidor-v2.yaml" => "sha256:8c39e692ff794ccb2587ebe142be040e44d76cbf970f45e65b28d56a6165bdb5",
15
15
  "nf-produto-v2.yaml" => "sha256:e565b47e4d8b17255f99efc2b6354d589d2903c4ba9b97caabd74f84de59e4e2",
16
+ "nf-servico-v1.yaml" => "sha256:621a3b8e437e5cb37367c8cd26fec93fa2b3f87c9a59252ac987c56bb0c7ba56",
16
17
  "nfeio.yaml" => "sha256:813bda287538f8599c3565485eb523d1b1311b26b5be94ead62ba0b7a17f6af3",
17
18
  "product-invoice-rtc-v1.yaml" => "sha256:4327ad141eeace6219dc4267678c44a134ae1fdde93f7dd69cf4c9ae9418415a",
18
19
  "product-register-pt-br-v1.yaml" => "sha256:beba0a3fb4dc1bc157a5a4a28e55768cea0e7390b491bdd4bedee2ee2297ca64",
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+ # AUTO-GENERATED — do not edit
3
+ # Source: openapi/nf-servico-v1.yaml
4
+ # Hash: sha256:621a3b8e437e5cb37367c8cd26fec93fa2b3f87c9a59252ac987c56bb0c7ba56
5
+
6
+ module Nfe
7
+ module Generated
8
+ module NfServicoV1
9
+ ErrorsResource = Data.define(:errors) do
10
+ def self.from_api(payload)
11
+ return nil if payload.nil?
12
+
13
+ new(
14
+ errors: payload["errors"],
15
+ )
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
data/lib/nfe/generated.rb CHANGED
@@ -362,6 +362,7 @@ require_relative "generated/nf_produto_v2/transport_vehicle_resource"
362
362
  require_relative "generated/nf_produto_v2/vehicle_detail_resource"
363
363
  require_relative "generated/nf_produto_v2/volume_resource"
364
364
  require_relative "generated/nf_produto_v2/withdrawal_information_resource"
365
+ require_relative "generated/nf_servico_v1/errors_resource"
365
366
  require_relative "generated/nfeio/batch_process_response"
366
367
  require_relative "generated/nfeio/environment"
367
368
  require_relative "generated/nfeio/file_parsing_options_request"
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nfe
4
+ # Immutable value object for an account-level webhook, as returned by the
5
+ # +/v2/webhooks+ API (account scope, envelope +{"webHook": {...}}+ on the
6
+ # wire). This is the shape the live API accepts and returns — confirmed by
7
+ # probe against +api.nfe.io+ (2026-07-02/03) and matching the schema in
8
+ # +openapi/nf-servico-v1.yaml+.
9
+ #
10
+ # Wire-format note: the OpenAPI spec declares +contentType+/+status+ as int
11
+ # enums (0/1), but the API serializes strings (+"json"+, +"Active"+) — this
12
+ # DTO follows the wire. +secret+ (32–64 chars) is echoed only on create and
13
+ # omitted on reads, so it is +nil+ on retrieve/list.
14
+ #
15
+ # {from_api} maps API camelCase onto snake_case, drops unknown keys, and is
16
+ # nil-tolerant (+from_api(nil)+ returns +nil+).
17
+ class AccountWebhook < Data.define(
18
+ :id,
19
+ :uri,
20
+ :content_type,
21
+ :secret,
22
+ :filters,
23
+ :insecure_ssl,
24
+ :headers,
25
+ :properties,
26
+ :status,
27
+ :created_on,
28
+ :modified_on
29
+ )
30
+ # @param payload [Hash, nil] the unwrapped webhook object.
31
+ # @return [Nfe::AccountWebhook, nil] +nil+ when +payload+ is +nil+.
32
+ def self.from_api(payload)
33
+ return nil if payload.nil?
34
+
35
+ new(
36
+ id: payload["id"],
37
+ uri: payload["uri"],
38
+ content_type: payload["contentType"],
39
+ secret: payload["secret"],
40
+ filters: payload["filters"],
41
+ insecure_ssl: payload["insecureSsl"],
42
+ headers: payload["headers"],
43
+ properties: payload["properties"],
44
+ status: payload["status"],
45
+ created_on: payload["createdOn"],
46
+ modified_on: payload["modifiedOn"]
47
+ )
48
+ end
49
+ end
50
+ end
@@ -1,17 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "nfe/resources/dto/service_invoice_borrower"
4
+
3
5
  module Nfe
4
6
  # Immutable value object for a service invoice (NFS-e) as returned by the
5
7
  # NFE.io +api.nfe.io/v1+ service-invoice API.
6
8
  #
7
- # The +nf-servico-v1.yaml+ OpenAPI spec defines NO component schemas (the
8
- # shape is derived from the operations), so this DTO is hand-written — mirror
9
- # of the fields the Node/PHP SDKs read. {from_api} maps the API camelCase keys
10
- # onto snake_case members, drops unknown keys, and is nil-tolerant
11
- # (+from_api(nil)+ returns +nil+).
9
+ # The +nf-servico-v1.yaml+ OpenAPI spec declares the retrieve success
10
+ # response **inline** (no named schema in +components.schemas+ only the
11
+ # error model is componentized), so this DTO is hand-written and pinned to
12
+ # the spec by an alignment test. {from_api} maps the API camelCase keys onto
13
+ # snake_case members and is nil-tolerant (+from_api(nil)+ returns +nil+).
14
+ # The full parsed payload is preserved under +raw+ for forward
15
+ # compatibility — fields without a typed member (the withholding tree,
16
+ # +provider+, +taxationType+, +location+, +approximateTax+, ...) are
17
+ # accessible through it.
18
+ #
19
+ # +borrower+ is hydrated into {Nfe::ServiceInvoiceBorrower} (typed readers
20
+ # plus a Hash-compatibility bridge, so +borrower["name"]+ keeps working).
12
21
  #
13
22
  # +flow_status+ drives the polling lifecycle; pass it to
14
23
  # {Nfe::FlowStatus.terminal?} to decide when the document is settled.
24
+ #
25
+ # @!attribute [r] pdf
26
+ # @deprecated Ghost field — the retrieve response carries no +pdf+ key
27
+ # (always +nil+). Use {Nfe::Resources::ServiceInvoices#download_pdf}.
28
+ # @!attribute [r] xml
29
+ # @deprecated Ghost field — the retrieve response carries no +xml+ key
30
+ # (always +nil+). Use {Nfe::Resources::ServiceInvoices#download_xml}.
15
31
  class ServiceInvoice < Data.define(
16
32
  :id,
17
33
  :flow_status,
@@ -33,12 +49,17 @@ module Nfe
33
49
  :pdf,
34
50
  :xml,
35
51
  :created_on,
36
- :modified_on
52
+ :modified_on,
53
+ :base_tax_amount,
54
+ :iss_rate,
55
+ :iss_tax_amount,
56
+ :raw
37
57
  )
38
58
  # Build a {Nfe::ServiceInvoice} from an API payload.
39
59
  #
40
60
  # @param payload [Hash, nil] the response object.
41
61
  # @return [Nfe::ServiceInvoice, nil] +nil+ when +payload+ is +nil+.
62
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize -- wide value-object mapping kept inline for Steep keyword-arg verification
42
63
  def self.from_api(payload)
43
64
  return nil if payload.nil?
44
65
 
@@ -56,15 +77,20 @@ module Nfe
56
77
  cancelled_on: payload["cancelledOn"],
57
78
  amount_net: payload["amountNet"],
58
79
  services_amount: payload["servicesAmount"],
59
- borrower: payload["borrower"],
80
+ borrower: ServiceInvoiceBorrower.from_api(payload["borrower"]),
60
81
  city_service_code: payload["cityServiceCode"],
61
82
  federal_service_code: payload["federalServiceCode"],
62
83
  description: payload["description"],
63
84
  pdf: payload["pdf"],
64
85
  xml: payload["xml"],
65
86
  created_on: payload["createdOn"],
66
- modified_on: payload["modifiedOn"]
87
+ modified_on: payload["modifiedOn"],
88
+ base_tax_amount: payload["baseTaxAmount"],
89
+ iss_rate: payload["issRate"],
90
+ iss_tax_amount: payload["issTaxAmount"],
91
+ raw: payload
67
92
  )
68
93
  end
94
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
69
95
  end
70
96
  end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "nfe/resources/dto/company"
4
+
5
+ module Nfe
6
+ # Immutable value object for the borrower (tomador) of a service invoice,
7
+ # as embedded in the NFS-e retrieve response (+nf-servico-v1.yaml+, inline
8
+ # schema of +GET /v1/companies/{company_id}/serviceinvoices/{id}+).
9
+ #
10
+ # +federal_tax_number+ is normalized to +String+ via {Nfe::Company.stringify}:
11
+ # the spec declares it +integer int64+, but the alphanumeric CNPJ
12
+ # (IN RFB 2.229/2024) requires string tolerance — deliberate deviation,
13
+ # pinned by the alignment test. +address+ stays a raw +Hash+.
14
+ #
15
+ # Hash-compatibility bridge: before this DTO existed, +invoice.borrower+
16
+ # returned the raw wire +Hash+ — {#[]} and {#dig} delegate to {#raw} so
17
+ # +borrower["name"]+ keeps working alongside the typed readers.
18
+ class ServiceInvoiceBorrower < Data.define(
19
+ :id,
20
+ :name,
21
+ :federal_tax_number,
22
+ :email,
23
+ :phone_number,
24
+ :address,
25
+ :parent_id,
26
+ :raw
27
+ )
28
+ # Build a {Nfe::ServiceInvoiceBorrower} from an API payload.
29
+ #
30
+ # @param payload [Hash, nil] the borrower object from the wire.
31
+ # @return [Nfe::ServiceInvoiceBorrower, nil] +nil+ when +payload+ is +nil+.
32
+ def self.from_api(payload)
33
+ return nil if payload.nil?
34
+
35
+ new(
36
+ id: payload["id"],
37
+ name: payload["name"],
38
+ federal_tax_number: Company.stringify(payload["federalTaxNumber"]),
39
+ email: payload["email"],
40
+ phone_number: payload["phoneNumber"],
41
+ address: payload["address"],
42
+ parent_id: payload["parentId"],
43
+ raw: payload
44
+ )
45
+ end
46
+
47
+ # Hash-style read, delegated to the raw wire payload (camelCase keys),
48
+ # e.g. +borrower["federalTaxNumber"]+ returns the wire value untouched.
49
+ def [](key)
50
+ raw && raw[key]
51
+ end
52
+
53
+ # Hash-style nested read over the raw wire payload,
54
+ # e.g. +borrower.dig("address", "city", "name")+.
55
+ def dig(*keys)
56
+ raw&.dig(*keys)
57
+ end
58
+ end
59
+ end
@@ -13,6 +13,12 @@ module Nfe
13
13
  #
14
14
  # {from_api} maps API camelCase onto snake_case, drops unknown keys, and is
15
15
  # nil-tolerant (+from_api(nil)+ returns +nil+).
16
+ #
17
+ # @deprecated This shape (+url+/+events+/+active+) is rejected by the live
18
+ # API (+400 "The Uri field is required"+), and the company-scoped route
19
+ # that would return it responds 404 (confirmed on three accounts,
20
+ # 2026-07-02/03). Use {Nfe::AccountWebhook} with the account-scoped
21
+ # methods on {Nfe::Resources::Webhooks}.
16
22
  class WebhookSubscription < Data.define(
17
23
  :id,
18
24
  :url,
@@ -2,17 +2,39 @@
2
2
 
3
3
  require "json"
4
4
  require "nfe/resources/abstract_resource"
5
+ require "nfe/resources/dto/account_webhook"
5
6
  require "nfe/resources/dto/webhook"
6
7
  require "nfe/webhook"
7
8
 
8
9
  module Nfe
9
10
  module Resources
10
- # Webhooks resource, company-scoped under +/companies/{id}/webhooks+ on the
11
- # +:main+ host family. Exposes CRUD, a synthetic-delivery +test+, the static
12
- # list of available events, and a thin {#verify_signature} delegation to
13
- # {Nfe::Webhook} (the canonical signature-verification API is the module).
11
+ # Webhooks resource on the +:main+ host family.
12
+ #
13
+ # Webhooks are managed at the **account** level over +/v2/webhooks+ — use
14
+ # the +*_account_webhook*+ methods. The live API wraps create/update
15
+ # requests and single-object responses in a +webHook+ envelope; the SDK
16
+ # envelopes/unwraps transparently. Contract source of truth:
17
+ # +openapi/nf-servico-v1.yaml+ plus live probes (2026-07-02/03).
18
+ #
19
+ # The company-scoped methods (+list+/+create+/+retrieve+/+update+/
20
+ # +delete+/+test+ under +/v1/companies/{id}/webhooks+) are deprecated: the
21
+ # route returns 404 on the current API (confirmed on three accounts,
22
+ # 2026-07-02/03). They remain, unchanged, until the next major.
23
+ #
24
+ # Also exposes a thin {#verify_signature} delegation to {Nfe::Webhook}
25
+ # (the canonical signature-verification API is the module).
14
26
  class Webhooks < AbstractResource
15
- # The seven event types the NFE.io API can deliver (parity with Node).
27
+ # Preserve the inherited HTTP DELETE helper under a private name before
28
+ # the public company-scoped +delete+ shadows it — the account-scoped
29
+ # deletes still need the raw verb.
30
+ alias http_delete delete
31
+ private :http_delete
32
+
33
+ # Legacy static list of event types.
34
+ #
35
+ # @deprecated These literals do not exist on the live API — the real
36
+ # event types follow +service_invoice.*+/+product_invoice.*+/
37
+ # +consumer_invoice.*+ (46 ids live). Use {#fetch_event_types}.
16
38
  AVAILABLE_EVENTS = %w[
17
39
  invoice.issued
18
40
  invoice.cancelled
@@ -29,10 +51,144 @@ module Nfe
29
51
  :main
30
52
  end
31
53
 
54
+ # Account-scoped endpoints live under +/v2+ on the same +:main+ host,
55
+ # while the (deprecated) company-scoped ones keep the resource default
56
+ # +/v1+. A path that already carries an explicit version segment passes
57
+ # through unprefixed.
58
+ def full_path(path)
59
+ path.start_with?("/v2/") ? path : super
60
+ end
61
+
32
62
  public
33
63
 
64
+ # List the account's webhooks (+GET /v2/webhooks+).
65
+ #
66
+ # The API wraps the collection as +{"webHooks": [...]}+; the SDK unwraps
67
+ # it into an {Nfe::ListResponse} of {Nfe::AccountWebhook}. +secret+ is
68
+ # omitted on reads.
69
+ #
70
+ # @return [Nfe::ListResponse]
71
+ def list_account_webhooks
72
+ response = get("/v2/webhooks")
73
+ hydrate_list(Nfe::AccountWebhook, parse_json(response.body), wrapper_key: "webHooks")
74
+ end
75
+
76
+ # Create an account webhook (+POST /v2/webhooks+).
77
+ #
78
+ # The request is wrapped in the mandatory +webHook+ envelope (the API
79
+ # rejects a bare body with +400 "missing required properties: 'webHook'"+)
80
+ # and the +201 {"webHook": {...}}+ response is unwrapped.
81
+ #
82
+ # NFE.io **pings the +uri+ at creation time and requires a 2xx
83
+ # response** — the endpoint must already be live, or the create fails.
84
+ # +secret+ must be 32–64 characters; it is echoed back on create and
85
+ # omitted on subsequent reads.
86
+ #
87
+ # @param data [Hash] webhook attributes in wire (camelCase) keys, e.g.
88
+ # +{ uri: "https://...", contentType: "json", secret: "<32-64 chars>",
89
+ # filters: ["service_invoice.issued_successfully"], status: "Active" }+.
90
+ # Discover valid +filters+ with {#fetch_event_types}.
91
+ # @return [Nfe::AccountWebhook]
92
+ def create_account_webhook(data)
93
+ response = post("/v2/webhooks",
94
+ body: json_body({ webHook: data }), headers: json_headers)
95
+ hydrate_account_webhook(response)
96
+ end
97
+
98
+ # Retrieve an account webhook by id (+GET /v2/webhooks/{id}+).
99
+ #
100
+ # Unwraps the +{"webHook": {...}}+ envelope, falling back to the raw
101
+ # body when the envelope is absent. +secret+ is omitted on reads.
102
+ #
103
+ # @param webhook_id [String]
104
+ # @return [Nfe::AccountWebhook]
105
+ def retrieve_account_webhook(webhook_id)
106
+ wid = Nfe::IdValidator.presence!(webhook_id, "webhook_id")
107
+ response = get("/v2/webhooks/#{wid}")
108
+ hydrate_account_webhook(response)
109
+ end
110
+
111
+ # Update an account webhook (+PUT /v2/webhooks/{id}+), request wrapped
112
+ # in the +webHook+ envelope.
113
+ #
114
+ # @note **+PUT+ is a full replacement** (live-confirmed 2026-07-03):
115
+ # omitted fields reset to their defaults — an update without +status+
116
+ # **deactivates the webhook**. Always send the complete object,
117
+ # starting from a retrieve:
118
+ #
119
+ # current = client.webhooks.retrieve_account_webhook(id)
120
+ # client.webhooks.update_account_webhook(id, {
121
+ # uri: current.uri,
122
+ # contentType: current.content_type,
123
+ # status: current.status, # keep it "Active"!
124
+ # filters: ["service_invoice.issued_successfully"]
125
+ # })
126
+ #
127
+ # @param webhook_id [String]
128
+ # @param data [Hash] the complete webhook attributes (wire camelCase keys).
129
+ # @return [Nfe::AccountWebhook]
130
+ def update_account_webhook(webhook_id, data)
131
+ wid = Nfe::IdValidator.presence!(webhook_id, "webhook_id")
132
+ response = put("/v2/webhooks/#{wid}",
133
+ body: json_body({ webHook: data }), headers: json_headers)
134
+ hydrate_account_webhook(response)
135
+ end
136
+
137
+ # Delete a single account webhook by id (+DELETE /v2/webhooks/{id}+).
138
+ #
139
+ # @param webhook_id [String]
140
+ # @return [nil]
141
+ def delete_account_webhook(webhook_id)
142
+ wid = Nfe::IdValidator.presence!(webhook_id, "webhook_id")
143
+ http_delete("/v2/webhooks/#{wid}")
144
+ nil
145
+ end
146
+
147
+ # ⚠️ DESTRUCTIVE: delete **ALL** of the account's webhooks
148
+ # (+DELETE /v2/webhooks+). Named distinctly from
149
+ # {#delete_account_webhook} so it can never be reached by a mistyped
150
+ # single delete.
151
+ #
152
+ # @return [nil]
153
+ def delete_all_account_webhooks
154
+ http_delete("/v2/webhooks")
155
+ nil
156
+ end
157
+
158
+ # Trigger a test ping for an account webhook
159
+ # (+PUT /v2/webhooks/{id}/pings+, responds 204).
160
+ #
161
+ # @param webhook_id [String]
162
+ # @return [nil]
163
+ def ping_account_webhook(webhook_id)
164
+ wid = Nfe::IdValidator.presence!(webhook_id, "webhook_id")
165
+ put("/v2/webhooks/#{wid}/pings", body: json_body({}), headers: json_headers)
166
+ nil
167
+ end
168
+
169
+ # Fetch the live list of webhook event types
170
+ # (+GET /v2/webhooks/eventTypes+).
171
+ #
172
+ # The API wraps the result as +{"eventTypes": [{ "id": ... }, ...]}+;
173
+ # this extracts the ids (e.g. +"service_invoice.issued_successfully"+,
174
+ # +"product_invoice.issued"+ — 46 ids live). Use these as +filters+ when
175
+ # creating or updating a webhook.
176
+ #
177
+ # @return [Array<String>]
178
+ def fetch_event_types
179
+ response = get("/v2/webhooks/eventTypes")
180
+ payload = parse_json(response.body)
181
+ items = [] #: Array[untyped]
182
+ items = payload["eventTypes"] || items if payload.is_a?(Hash)
183
+ items.map { |item| item["id"] }.compact
184
+ end
185
+
34
186
  # List a company's webhook subscriptions.
35
187
  #
188
+ # @deprecated The +/v1/companies/{id}/webhooks+ route returns 404 on the
189
+ # current API (confirmed on three accounts, 2026-07-02/03). Use
190
+ # {#list_account_webhooks}.
191
+ #
36
192
  # @param company_id [String]
37
193
  # @return [Nfe::ListResponse]
38
194
  def list(company_id)
@@ -45,9 +201,13 @@ module Nfe
45
201
 
46
202
  # Create a webhook subscription. Accepts +url+, +events+, +secret+, +active+.
47
203
  #
204
+ # @deprecated The +/v1/companies/{id}/webhooks+ route returns 404 on the
205
+ # current API (confirmed on three accounts, 2026-07-02/03). Use
206
+ # {#create_account_webhook}.
207
+ #
48
208
  # @param company_id [String]
49
209
  # @param data [Hash]
50
- # @return [Nfe::Webhook]
210
+ # @return [Nfe::WebhookSubscription, nil]
51
211
  def create(company_id, data)
52
212
  id = Nfe::IdValidator.company_id(company_id)
53
213
  response = post("/companies/#{id}/webhooks",
@@ -57,9 +217,13 @@ module Nfe
57
217
 
58
218
  # Retrieve a webhook by id.
59
219
  #
220
+ # @deprecated The +/v1/companies/{id}/webhooks+ route returns 404 on the
221
+ # current API (confirmed on three accounts, 2026-07-02/03). Use
222
+ # {#retrieve_account_webhook}.
223
+ #
60
224
  # @param company_id [String]
61
225
  # @param webhook_id [String]
62
- # @return [Nfe::Webhook]
226
+ # @return [Nfe::WebhookSubscription, nil]
63
227
  def retrieve(company_id, webhook_id)
64
228
  id = Nfe::IdValidator.company_id(company_id)
65
229
  wid = Nfe::IdValidator.presence!(webhook_id, "webhook_id")
@@ -69,10 +233,14 @@ module Nfe
69
233
 
70
234
  # Update a webhook.
71
235
  #
236
+ # @deprecated The +/v1/companies/{id}/webhooks+ route returns 404 on the
237
+ # current API (confirmed on three accounts, 2026-07-02/03). Use
238
+ # {#update_account_webhook}.
239
+ #
72
240
  # @param company_id [String]
73
241
  # @param webhook_id [String]
74
242
  # @param data [Hash]
75
- # @return [Nfe::Webhook]
243
+ # @return [Nfe::WebhookSubscription, nil]
76
244
  def update(company_id, webhook_id, data)
77
245
  id = Nfe::IdValidator.company_id(company_id)
78
246
  wid = Nfe::IdValidator.presence!(webhook_id, "webhook_id")
@@ -83,6 +251,10 @@ module Nfe
83
251
 
84
252
  # Delete a webhook.
85
253
  #
254
+ # @deprecated The +/v1/companies/{id}/webhooks+ route returns 404 on the
255
+ # current API (confirmed on three accounts, 2026-07-02/03). Use
256
+ # {#delete_account_webhook}.
257
+ #
86
258
  # @param company_id [String]
87
259
  # @param webhook_id [String]
88
260
  # @return [nil]
@@ -95,6 +267,10 @@ module Nfe
95
267
 
96
268
  # Trigger a synthetic delivery to verify a webhook is reachable.
97
269
  #
270
+ # @deprecated The +/v1/companies/{id}/webhooks+ route returns 404 on the
271
+ # current API (confirmed on three accounts, 2026-07-02/03). Use
272
+ # {#ping_account_webhook}.
273
+ #
98
274
  # @param company_id [String]
99
275
  # @param webhook_id [String]
100
276
  # @return [Hash] +{ success: bool, message: String? }+.
@@ -107,7 +283,10 @@ module Nfe
107
283
  { success: payload["success"] || false, message: payload["message"] }
108
284
  end
109
285
 
110
- # The static list of webhook event types the API can deliver.
286
+ # The legacy static list of webhook event types.
287
+ #
288
+ # @deprecated These literals do not exist on the live API. Use
289
+ # {#fetch_event_types} for the real, live list.
111
290
  #
112
291
  # @return [Array<String>]
113
292
  def get_available_events
@@ -124,6 +303,12 @@ module Nfe
124
303
 
125
304
  private
126
305
 
306
+ # Unwrap a single-object +{"webHook": {...}}+ envelope (raw-body
307
+ # fallback) and hydrate an {Nfe::AccountWebhook}.
308
+ def hydrate_account_webhook(response)
309
+ hydrate(Nfe::AccountWebhook, unwrap(parse_json(response.body), "webHook"))
310
+ end
311
+
127
312
  def json_headers
128
313
  { "Content-Type" => "application/json" }
129
314
  end
@@ -133,7 +318,7 @@ module Nfe
133
318
  end
134
319
 
135
320
  # Tolerate either a bare array, a +data+-wrapped list, or a +webhooks+
136
- # envelope for the list response.
321
+ # envelope for the (deprecated) company-scoped list response.
137
322
  def webhook_items(payload)
138
323
  return payload if payload.is_a?(Array)
139
324
  return [] unless payload.is_a?(Hash)
data/lib/nfe/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nfe
4
- VERSION = "1.0.0"
4
+ VERSION = "1.1.0"
5
5
  end
@@ -4,7 +4,7 @@ module Nfe
4
4
  # Immutable value object for a verified webhook delivery, produced by
5
5
  # {Nfe::Webhook.construct_event} after the HMAC-SHA1 signature checks out.
6
6
  #
7
- # - +type+ is the event type (e.g. +"invoice.issued"+), unwrapped from the
7
+ # - +type+ is the event type (e.g. +"service_invoice.issued_successfully"+), unwrapped from the
8
8
  # delivery envelope's +action+ or +event+ key.
9
9
  # - +data+ is the payload +Hash+ (the envelope's +payload+ or +data+ key).
10
10
  # - +id+ is a stable event/invoice id for deduplication, or +nil+ when the
@@ -0,0 +1,16 @@
1
+ # AUTO-GENERATED — do not edit
2
+ # Source: openapi/nf-servico-v1.yaml
3
+ # Hash: sha256:621a3b8e437e5cb37367c8cd26fec93fa2b3f87c9a59252ac987c56bb0c7ba56
4
+
5
+ module Nfe
6
+ module Generated
7
+ module NfServicoV1
8
+ class ErrorsResource < Data
9
+ attr_reader errors: Array[Hash[String, untyped]]
10
+ def self.new: (?errors: Array[Hash[String, untyped]]) -> instance
11
+ def initialize: (?errors: Array[Hash[String, untyped]]) -> void
12
+ def self.from_api: (Hash[String, untyped]? payload) -> instance?
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,21 @@
1
+ module Nfe
2
+ class AccountWebhook < Data
3
+ attr_reader id: String?
4
+ attr_reader uri: String?
5
+ attr_reader content_type: String?
6
+ attr_reader secret: String?
7
+ attr_reader filters: Array[String]?
8
+ attr_reader insecure_ssl: bool?
9
+ attr_reader headers: untyped
10
+ attr_reader properties: untyped
11
+ attr_reader status: String?
12
+ attr_reader created_on: String?
13
+ attr_reader modified_on: String?
14
+
15
+ def self.from_api: (Hash[untyped, untyped]? payload) -> Nfe::AccountWebhook?
16
+
17
+ def self.new: (id: String?, uri: String?, content_type: String?, secret: String?, filters: Array[String]?, insecure_ssl: bool?, headers: untyped, properties: untyped, status: String?, created_on: String?, modified_on: String?) -> instance
18
+
19
+ def initialize: (id: String?, uri: String?, content_type: String?, secret: String?, filters: Array[String]?, insecure_ssl: bool?, headers: untyped, properties: untyped, status: String?, created_on: String?, modified_on: String?) -> void
20
+ end
21
+ end
@@ -13,7 +13,7 @@ module Nfe
13
13
  attr_reader cancelled_on: String?
14
14
  attr_reader amount_net: untyped
15
15
  attr_reader services_amount: untyped
16
- attr_reader borrower: untyped
16
+ attr_reader borrower: Nfe::ServiceInvoiceBorrower?
17
17
  attr_reader city_service_code: untyped
18
18
  attr_reader federal_service_code: untyped
19
19
  attr_reader description: String?
@@ -21,10 +21,14 @@ module Nfe
21
21
  attr_reader xml: untyped
22
22
  attr_reader created_on: String?
23
23
  attr_reader modified_on: String?
24
+ attr_reader base_tax_amount: (Float | Integer)?
25
+ attr_reader iss_rate: (Float | Integer)?
26
+ attr_reader iss_tax_amount: (Float | Integer)?
27
+ attr_reader raw: Hash[untyped, untyped]?
24
28
 
25
29
  def self.from_api: (Hash[untyped, untyped]? payload) -> Nfe::ServiceInvoice?
26
30
 
27
- def self.new: (id: String?, flow_status: String?, flow_message: String?, status: untyped, environment: untyped, rps_number: untyped, rps_serial_number: untyped, number: untyped, check_code: String?, issued_on: String?, cancelled_on: String?, amount_net: untyped, services_amount: untyped, borrower: untyped, city_service_code: untyped, federal_service_code: untyped, description: String?, pdf: untyped, xml: untyped, created_on: String?, modified_on: String?) -> instance
28
- def initialize: (id: String?, flow_status: String?, flow_message: String?, status: untyped, environment: untyped, rps_number: untyped, rps_serial_number: untyped, number: untyped, check_code: String?, issued_on: String?, cancelled_on: String?, amount_net: untyped, services_amount: untyped, borrower: untyped, city_service_code: untyped, federal_service_code: untyped, description: String?, pdf: untyped, xml: untyped, created_on: String?, modified_on: String?) -> void
31
+ def self.new: (id: String?, flow_status: String?, flow_message: String?, status: untyped, environment: untyped, rps_number: untyped, rps_serial_number: untyped, number: untyped, check_code: String?, issued_on: String?, cancelled_on: String?, amount_net: untyped, services_amount: untyped, borrower: Nfe::ServiceInvoiceBorrower?, city_service_code: untyped, federal_service_code: untyped, description: String?, pdf: untyped, xml: untyped, created_on: String?, modified_on: String?, base_tax_amount: (Float | Integer)?, iss_rate: (Float | Integer)?, iss_tax_amount: (Float | Integer)?, raw: Hash[untyped, untyped]?) -> instance
32
+ def initialize: (id: String?, flow_status: String?, flow_message: String?, status: untyped, environment: untyped, rps_number: untyped, rps_serial_number: untyped, number: untyped, check_code: String?, issued_on: String?, cancelled_on: String?, amount_net: untyped, services_amount: untyped, borrower: Nfe::ServiceInvoiceBorrower?, city_service_code: untyped, federal_service_code: untyped, description: String?, pdf: untyped, xml: untyped, created_on: String?, modified_on: String?, base_tax_amount: (Float | Integer)?, iss_rate: (Float | Integer)?, iss_tax_amount: (Float | Integer)?, raw: Hash[untyped, untyped]?) -> void
29
33
  end
30
34
  end
@@ -0,0 +1,21 @@
1
+ module Nfe
2
+ class ServiceInvoiceBorrower < Data
3
+ attr_reader id: String?
4
+ attr_reader name: String?
5
+ attr_reader federal_tax_number: String?
6
+ attr_reader email: String?
7
+ attr_reader phone_number: String?
8
+ attr_reader address: untyped
9
+ attr_reader parent_id: String?
10
+ attr_reader raw: Hash[untyped, untyped]?
11
+
12
+ def self.from_api: (Hash[untyped, untyped]? payload) -> Nfe::ServiceInvoiceBorrower?
13
+
14
+ def self.new: (id: String?, name: String?, federal_tax_number: String?, email: String?, phone_number: String?, address: untyped, parent_id: String?, raw: Hash[untyped, untyped]?) -> instance
15
+
16
+ def initialize: (id: String?, name: String?, federal_tax_number: String?, email: String?, phone_number: String?, address: untyped, parent_id: String?, raw: Hash[untyped, untyped]?) -> void
17
+
18
+ def []: (untyped key) -> untyped
19
+ def dig: (*untyped keys) -> untyped
20
+ end
21
+ end
@@ -4,6 +4,16 @@ module Nfe
4
4
  AVAILABLE_EVENTS: Array[String]
5
5
 
6
6
  def api_family: () -> Symbol
7
+ def full_path: (String path) -> String
8
+
9
+ def list_account_webhooks: () -> Nfe::ListResponse
10
+ def create_account_webhook: (Hash[untyped, untyped] data) -> Nfe::AccountWebhook?
11
+ def retrieve_account_webhook: (String webhook_id) -> Nfe::AccountWebhook?
12
+ def update_account_webhook: (String webhook_id, Hash[untyped, untyped] data) -> Nfe::AccountWebhook?
13
+ def delete_account_webhook: (String webhook_id) -> nil
14
+ def delete_all_account_webhooks: () -> nil
15
+ def ping_account_webhook: (String webhook_id) -> nil
16
+ def fetch_event_types: () -> Array[String]
7
17
 
8
18
  def list: (String company_id) -> Nfe::ListResponse
9
19
  def create: (String company_id, Hash[untyped, untyped] data) -> Nfe::WebhookSubscription?
@@ -16,6 +26,8 @@ module Nfe
16
26
 
17
27
  private
18
28
 
29
+ def http_delete: (String path, ?query: Hash[untyped, untyped], ?request_options: Nfe::RequestOptions?, ?headers: Hash[String, String]) -> Nfe::Http::Response
30
+ def hydrate_account_webhook: (Nfe::Http::Response response) -> Nfe::AccountWebhook?
19
31
  def json_headers: () -> Hash[String, String]
20
32
  def json_body: (untyped data) -> String
21
33
  def webhook_items: (untyped payload) -> Array[untyped]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nfe-io
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - NFE.io Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-07-02 00:00:00.000000000 Z
11
+ date: 2026-07-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -505,6 +505,7 @@ files:
505
505
  - lib/nfe/generated/nf_produto_v2/vehicle_detail_resource.rb
506
506
  - lib/nfe/generated/nf_produto_v2/volume_resource.rb
507
507
  - lib/nfe/generated/nf_produto_v2/withdrawal_information_resource.rb
508
+ - lib/nfe/generated/nf_servico_v1/errors_resource.rb
508
509
  - lib/nfe/generated/nfeio/batch_process_response.rb
509
510
  - lib/nfe/generated/nfeio/environment.rb
510
511
  - lib/nfe/generated/nfeio/file_parsing_options_request.rb
@@ -710,6 +711,7 @@ files:
710
711
  - lib/nfe/resources/companies.rb
711
712
  - lib/nfe/resources/consumer_invoice_query.rb
712
713
  - lib/nfe/resources/consumer_invoices.rb
714
+ - lib/nfe/resources/dto/account_webhook.rb
713
715
  - lib/nfe/resources/dto/addresses/address_lookup_response.rb
714
716
  - lib/nfe/resources/dto/company.rb
715
717
  - lib/nfe/resources/dto/consumer_invoice.rb
@@ -726,6 +728,7 @@ files:
726
728
  - lib/nfe/resources/dto/product_invoice_query/product_invoice_details.rb
727
729
  - lib/nfe/resources/dto/product_invoice_query/product_invoice_events_response.rb
728
730
  - lib/nfe/resources/dto/service_invoice.rb
731
+ - lib/nfe/resources/dto/service_invoice_borrower.rb
729
732
  - lib/nfe/resources/dto/state_taxes/nfe_state_tax.rb
730
733
  - lib/nfe/resources/dto/tax_codes/tax_code_paginated_response.rb
731
734
  - lib/nfe/resources/dto/webhook.rb
@@ -1126,6 +1129,7 @@ files:
1126
1129
  - sig/nfe/generated/nf_produto_v2/vehicle_detail_resource.rbs
1127
1130
  - sig/nfe/generated/nf_produto_v2/volume_resource.rbs
1128
1131
  - sig/nfe/generated/nf_produto_v2/withdrawal_information_resource.rbs
1132
+ - sig/nfe/generated/nf_servico_v1/errors_resource.rbs
1129
1133
  - sig/nfe/generated/nfeio/batch_process_response.rbs
1130
1134
  - sig/nfe/generated/nfeio/environment.rbs
1131
1135
  - sig/nfe/generated/nfeio/file_parsing_options_request.rbs
@@ -1333,6 +1337,7 @@ files:
1333
1337
  - sig/nfe/resources/consumer_invoice_pending.rbs
1334
1338
  - sig/nfe/resources/consumer_invoice_query.rbs
1335
1339
  - sig/nfe/resources/consumer_invoices.rbs
1340
+ - sig/nfe/resources/dto/account_webhook.rbs
1336
1341
  - sig/nfe/resources/dto/addresses/address_lookup_response.rbs
1337
1342
  - sig/nfe/resources/dto/company.rbs
1338
1343
  - sig/nfe/resources/dto/consumer_invoice.rbs
@@ -1349,6 +1354,7 @@ files:
1349
1354
  - sig/nfe/resources/dto/product_invoice_query/product_invoice_details.rbs
1350
1355
  - sig/nfe/resources/dto/product_invoice_query/product_invoice_events_response.rbs
1351
1356
  - sig/nfe/resources/dto/service_invoice.rbs
1357
+ - sig/nfe/resources/dto/service_invoice_borrower.rbs
1352
1358
  - sig/nfe/resources/dto/state_taxes/nfe_state_tax.rbs
1353
1359
  - sig/nfe/resources/dto/tax_codes/tax_code_paginated_response.rbs
1354
1360
  - sig/nfe/resources/dto/webhook.rbs