Skip to content

Electronic invoicing with Ruby

This tutorial builds three simple Ruby applications from scratch:

  1. Receive: connects and authenticates with the Invoicetronic API and downloads any new incoming invoices.
  2. Send: connects and authenticates with the Invoicetronic API and sends an invoice to the SDI.
  3. Update: connects and authenticates with the Invoicetronic API and consults the history of notifications returned by the SDI.

Before continuing, make sure all the prerequisites below are met.

Prerequisites

We assume that these prerequisites are met:

Tip

For an optimal Ruby experience, consider using rbenv or RVM for Ruby version management.

Did you know?

The Ruby SDK is compatible with Rails, Sinatra, and all modern Ruby applications.

Receive

Create the app

The first step is to create the application directory:

mkdir receive && cd receive

Install the SDK

Create a Gemfile with the following content:

Gemfile
source 'https://rubygems.org'

gem 'invoicetronic_sdk'

Then install the dependencies:

bundle install

Configure the SDK

Create the file receive.rb:

Configure the SDK
require 'invoicetronic_sdk'

# Configure the SDK
InvoicetronicSdk.configure do |config|
  config.host = 'https://api.invoicetronic.com/v1'
  config.username = 'YOUR TEST API KEY (starts with ik_test_)'
  config.password = ''
end

As you can see, we configure the SDK by setting the host and HTTP Basic authentication with your test API Key (not the live one). Notice how we use username for the API Key and password empty.

API Key comes in pairs

When you create your account, you obtain a pair of API Keys. One is the test key for the API Sandbox, and the other is the live API's. You can tell the difference because the former starts with ik_test_, while the latter begins with ik_live_. In this tutorial, always use the test key.

Download invoices

We are ready to make a request. We want to download new vendor invoices that may be available from the SDI. Add this code to the file:

Download unread invoices
require 'base64'

# Download unread invoices
api_instance = InvoicetronicSdk::ReceiveApi.new

begin
  inbound_invoices = api_instance.receive_get(
    nil,    # company_id
    nil,    # identifier
    true,   # unread
    nil,    # committente
    nil,    # prestatore
    nil,    # file_name
    nil,    # last_update_from
    nil,    # last_update_to
    nil,    # date_sent_from
    nil,    # date_sent_to
    nil,    # document_date_from
    nil,    # document_date_to
    nil,    # document_number
    true,   # include_payload
    nil,    # page
    nil,    # page_size
    nil     # sort
  )

  puts "Received #{inbound_invoices.length} invoices"

  inbound_invoices.each do |invoice|
    if invoice.encoding == 'Xml'
      File.write(invoice.file_name, invoice.payload)
    elsif invoice.encoding == 'Base64'
      File.write(invoice.file_name, Base64.decode64(invoice.payload))
    end

    puts "Downloaded #{invoice.file_name} from a vendor with VAT ID #{invoice.prestatore}"
  end
rescue InvoicetronicSdk::ApiError => e
  puts "Error: #{e}"
end

Payload Inclusion

We set include_payload to true to retrieve the actual invoice content in the payload property. Without this parameter, the payload field would be nil by default, which increases performance and reduces response size when you only need metadata.

Run the application:

ruby receive.rb

You should obtain an output similar to this one:

Received 3 invoices
Downloaded file1.xml from a vendor with VAT ID IT06157670966
Downloaded file2.xml.p7m from a vendor with VAT ID IT01280270057
Downloaded file3.xml.p7m from a vendor with VAT ID IT01280270057

The files are in the current directory, ready for you to inspect them.

Not receiving invoices in the live environment?

Ensure you registered with the Italian Revenue Service, which is a requirement for the live environment.

What we learned

In this example, we learned several things.

  1. We must configure the SDK using InvoicetronicSdk.configure, setting the host and configuring HTTP Basic authentication with username (API key) and empty password.

  2. We must instantiate a class representing the endpoint we want to work with. In this case, we leverage ReceiveApi to download incoming invoices.

  3. Endpoint classes like ReceiveApi offer methods for interacting with their target entity. We call receive_get to retrieve invoices. Because we only want new, unread invoices, we pass true for the unread parameter. We also pass true for include_payload to retrieve the actual invoice content.

  4. Invoice objects expose properties like encoding, file_name, and payload. The last one contains the invoice content, as plain text or Base64-encoded, as described by encoding which has the values 'Xml' or 'Base64'.

Source Code on GitHub

The source code for this Quickstart is also available on GitHub.

Send

Create the app

The first step is to create the application directory:

mkdir send && cd send

Install the SDK

Create a Gemfile with the following content:

Gemfile
source 'https://rubygems.org'

gem 'invoicetronic_sdk'

Then install the dependencies:

bundle install

Configure the SDK

Create the file send.rb:

Configure the SDK
require 'invoicetronic_sdk'

# Configure the SDK
InvoicetronicSdk.configure do |config|
  config.host = 'https://api.invoicetronic.com/v1'
  config.username = 'YOUR TEST API KEY (starts with ik_test_)'
  config.password = ''
end

As you can see, we configure the SDK by setting the host and HTTP Basic authentication with your test API Key (not the live one).

API Key comes in pairs

When you create your account, you obtain a pair of API Keys. One is the test key for the API Sandbox, and the other is the live API's. You can tell the difference because the former starts with ik_test_, while the latter begins with ik_live_. In this tutorial, always use the test key.

Send an invoice

We are ready to make a request. We want to send an invoice to the SDI. Add this code to the file:

Send an invoice
# Send an invoice
file_path = '/some/file/path/filename.xml'

meta_data = {
  'internal_id' => '123',
  'created_with' => 'myapp',
  'some_other_custom_data' => 'value'
}

api_instance = InvoicetronicSdk::SendApi.new

begin
  payload = File.read(file_path)

  send_data = InvoicetronicSdk::Send.new(
    file_name: File.basename(file_path),
    payload: payload,
    meta_data: meta_data
  )

  sent_invoice = api_instance.send_post(send_data)

  puts "The invoice was sent successfully, it now has the unique Id of #{sent_invoice.id}."
rescue InvoicetronicSdk::ApiError => e
  puts "Error: #{e}"
end

Run the application:

ruby send.rb

You should obtain an output similar to this one:

The invoice was sent successfully, it now has the unique Id of 123.

Check the invoice state

When you forward an invoice to the SDI, delivery is not instantaneous: the SDI runs a series of checks and returns a sequence of notifications that describe the state of the process (Inviato, Consegnato, Scartato, etc.). The Send model exposes a latest_state attribute with the current state, sparing you a separate /update call when you only need to know how it went.

Read the current state
# Fetch the most recent state of an already-sent invoice
fresh = api_instance.send_id_get(sent_invoice.id)
puts "Current state: #{fresh.latest_state || 'Processing'}"

Right after submission, latest_state may be nil: the SDI has not processed the document yet. Check again after a few seconds or, better, configure a webhook to receive a push notification on every state change.

Save API calls

Use latest_state on Send whenever you only need the current state: a single call instead of one to /send plus one to /update. Reach for UpdateApi only when you need the full transition history.

What we learned

In this example, we learned several things.

  1. We must configure the SDK using InvoicetronicSdk.configure, setting the host and configuring HTTP Basic authentication.

  2. We must instantiate a class representing the endpoint we want to work with. In this case, we leverage SendApi to send invoices. Endpoint classes like SendApi offer methods for interacting with their target entity. We call send_post to send an invoice.

  3. The Send model accepts parameters in the constructor: file_name, payload, and meta_data. The payload contains the invoice content, while meta_data is optional and binds custom data to the document.

  4. The Send model also exposes latest_state with the current SDI state, readable via send_id_get(id). It saves a /update call when you only need to know the state.

Source Code on GitHub

The source code for this Quickstart is also available on GitHub.

Update

For the current state of a sent invoice, just read latest_state from the Send model (see Check the invoice state). If instead you need the full transition history — for example to understand why an invoice was rejected, render every state transition with timestamps in your UI, or track the notifications returned by a public administration entity — use UpdateApi.

/update queries are free of charge

Requests to /update are not counted against your plan: you can poll the notification history as often as you need.

Create the application

mkdir update && cd update

Install the SDK

Create a Gemfile with the following contents:

Gemfile
source 'https://rubygems.org'

gem 'invoicetronic_sdk'

Then install dependencies:

bundle install

Retrieve the notification history

Create the file update.rb:

Notification history for an invoice
require 'invoicetronic_sdk'

# Configure the SDK
InvoicetronicSdk.configure do |config|
  config.host = 'https://api.invoicetronic.com/v1'
  config.username = 'YOUR TEST API KEY (starts with ik_test_)'
  config.password = ''
end

# Id of the sent invoice we want to inspect
send_id = 225

api_instance = InvoicetronicSdk::UpdateApi.new

begin
  updates = api_instance.update_get(
    nil,           # company_id
    nil,           # identifier
    nil,           # prestatore
    nil,           # unread
    send_id,       # send_id
    nil,           # state
    nil,           # last_update_from
    nil,           # last_update_to
    nil,           # date_sent_from
    nil,           # date_sent_to
    nil,           # page
    nil,           # page_size
    'last_update'  # sort
  )

  puts "Found #{updates.length} notifications for invoice #{send_id}"

  updates.each do |update|
    description = update.description || 'OK'
    puts "  [#{update.last_update}] state=#{update.state} - #{description}"
  end
rescue InvoicetronicSdk::ApiError => e
  puts "Error: #{e}"
end

Run the application:

ruby update.rb

You should obtain an output similar to this one:

Found 2 notifications for invoice 225
  [2025-01-23 16:56:14 UTC] state=Inviato - OK
  [2025-01-23 17:12:03 UTC] state=Consegnato - OK

The state field is the most important property. The most common values are:

Value Name Description
2 Inviato Sent to the SDI.
5 Consegnato Delivered to the recipient.
7 Scartato Rejected by the SDI. The reason is in description.

The complete list of values is available in the API Reference.

Always monitor the state of your sent invoices

A state of Inviato only means that the document has been accepted by the SDI, not that it has been delivered. A Scartato state indicates that the invoice was not accepted and may require a correction and a fresh submission.

What we learned

  1. To consult the notification history we use the UpdateApi class instead of SendApi or ReceiveApi.

  2. The update_get() method accepts filters such as send_id (notifications for a specific sent invoice), state (filter by state), last_update_from/last_update_to (date range) and others.

  3. /update queries are free of charge and do not count against your plan, so you can poll them as often as you need.

Source Code on GitHub

The source code for this Quickstart is also available on GitHub.