Ruby SDK
The official fiscalrail gem supports all 42 operations in the API reference. Version 0.4.0 requires Ruby 3.3 or later and works with or without Rails. Source code and releases are available on GitHub.
Install and authenticate
Add the gem to your Gemfile and run bundle install:
gem "fiscalrail", "~> 0.4.0"
Create a client with an explicit API key:
require "fiscalrail"
client = FiscalRail::Client.new(api_key: ENV.fetch("FISCALRAIL_API_KEY"))
Your application reads the key from its environment or secret manager. The SDK does not read it automatically. Test and Live use the same base URL; the key selects the account environment.
Reuse the client for the lifetime of your application process and call client.close on shutdown. Its default HTTP adapter reuses a connection and serializes concurrent requests. Use separate clients when you need concurrent HTTP requests. FiscalRail::Client.open(api_key: key) { |client| ... } closes the connection when the block finishes. Custom adapter: instances remain owned by your application.
Issue an invoice
Use BigDecimal or strings for monetary input and Spain's tax helpers for catalog-backed taxes:
require "fiscalrail"
client = FiscalRail::Client.new(api_key: ENV.fetch("FISCALRAIL_API_KEY"))
invoice = client.invoices.issue(
idempotency_key: "2f294ef2-9a60-4c7e-a573-5e18fa8348e2",
lines: [{ description: "Consulting", quantity: 2, unit_price: BigDecimal("75.00"),
taxes: [FiscalRail::TaxRegimes::ES::VAT.general] }]
)
Requests accept keyword arguments and nested hashes with symbol or string keys. Omitted fields stay omitted; explicit nil, false and empty arrays are preserved. Dates and times are serialized as ISO 8601 strings. Business validation stays on the API.
Response models
Responses are generated, read-only objects under FiscalRail::Models. Dates become Date, timestamps become Time and decimal strings become BigDecimal:
invoice.id
invoice.issue_date
invoice.totals.payable
invoice.request_id
invoice.idempotency_key
invoice.idempotent_replayed
invoice.to_h
Nested values are frozen. Unknown fields remain available through extra_fields and response["field"]; to_h returns fresh containers with string keys. Invalid response structure raises FiscalRail::ResponseParseError with the field path and request ID.
Idempotency and retries
Invoice issue and amend generate an idempotency key when none is supplied. Retries within that call reuse it. For durable jobs, persist your own idempotency_key: before the first attempt and supply it on every retry after a process restart. A new call without a supplied key generates a new key. Never reuse a key for a different operation or payload.
The default is at most two retries for connection failures, timeouts, HTTP 408, 429 and 5xx, only for safe operations: reads, idempotency-protected issuance and amendments, PDF rendering, and destination enable/disable. Ordinary create, update and delete calls are not automatically retried. The client honors Retry-After, bounded to 30 seconds, and otherwise uses exponential backoff. Set max_retries: 0 to disable retries.
Errors
All SDK errors inherit from FiscalRail::Error. API errors expose status_code, code, request_id, details and idempotency_key:
begin
invoice = client.invoices.retrieve("inv_...")
rescue FiscalRail::APIConnectionError => error
warn error.message
rescue FiscalRail::APIError => error
warn "#{error.code}: HTTP #{error.status_code}, request #{error.request_id}"
end
Connection and timeout errors also preserve the idempotency key. Keep it when the result of an issuance or amendment is uncertain.
Available resources
The client exposes account, account_tax_regimes, balances, api_keys, customers, event_destinations, events, invoice_series, payment_instructions, invoices, invoice_pdfs, tax_ids and tax_regimes. Each operation in the API reference includes a Ruby example. Invoices use issue and amend; issued invoices are never updated.
The singleton account routes require an SDK release generated from the current OpenAPI contract. Existing released SDK versions still use account IDs.
Retrieve account-specific state with client.balances.retrieve() and client.account_tax_regimes.retrieve(). The separate tax_regimes resource describes the public catalog.
Pagination
list retrieves one page. Use auto_paging_each to fetch subsequent pages lazily while preserving filters:
page = client.customers.list(country: "ES", limit: 25)
page.each { |customer| puts customer.name }
client.invoices.auto_paging_each(customer: "cus_...", page_size: 100) do |invoice|
puts invoice.code
end
Pages expose data and has_more. For manual cursors, pass starting_after: or ending_before: to list. Tax regimes return a single list and do not expose automatic pagination.
Invoice PDFs
render and retrieve return PDF metadata, including a customer-facing URL. To download the bytes directly:
client.invoice_pdfs.render_content(invoice.id, locale: "en").write_to_file("invoice.pdf")
The _content methods return FiscalRail::BinaryContent. Downloads are buffered in memory. locale: becomes the Accept-Language header.
Verify webhooks
Verify the unmodified body before processing an event. In a Rails controller:
event = FiscalRail::Webhooks.construct_event(
request.raw_post,
request.headers["FiscalRail-Signature"],
signing_secret
)
The helper verifies the HMAC in constant time, rejects timestamps more than five minutes in either direction and then parses the event into a hash with string keys. Verification failure raises FiscalRail::WebhookSignatureError. See webhooks for delivery and acknowledgement behavior.