curl
Quickstart
curl is used in command lines or scripts to transfer data. It is ubiquitous and used daily by virtually every Internet-using human, albeit probably unbeknownst to them.
One obvious advantage of curl is that you don't need a programming language (and all the toolchain that comes with it) to work with the Invoicetronic API. Also, curl is scriptable, meaning you can automate tasks via cronjobs or other means.
In this quickstart we'll use curl to:
Before continuing, make sure all the prerequisites below are met.
Prerequisites
We assume that these prerequisites are met:
- curl is installed on your system.
- You obtained an active API Key.
- jq is installed on your system (optional, but it'll greatly improve your experience.)
- You registered with the Italian Revenue Service (needed for the live environment)
Send
With curl
we can leverage the send/file
API endpoint to upload a file:
curl -u "YOUR TEST API KEY (starts with ik_test_)": \
-F "file=@path/to/file/filename.xml" \
https://api.invoicetronic.com/v1/send/file | jq
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.
A few things of note:
- We add a colon following the API key. It prevents curl from asking for the password. If you forget the colon, curl will go interactive and ask for a password: leave it blank.
- Don't forget to start your file path with the leading
@
. - The
| jq
pipe is not strictly required, but it will output a pleasantly readable JSON. In a script, you might want to leave it out.
Thanks to the jq pipe, we obtain a JSON response in our console output:
{
"meta_data": null,
...
"user_id": 1,
"company_id": 13,
"committente": "IT01234567891",
"prestatore": "IT06157670966",
"identifier": null,
"file_name": "filename.xml",
"format": "FPR12",
"payload": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\...",
"last_update": null,
"date_sent": null,
"documents": [
{
"number": "IT2368735",
"date": "2024-05-31T22:00:00Z"
}
],
"encoding": "Xml",
"id": 225,
"created": "2025-01-23T16:55:51.491271Z",
}
If we send the same request again, we obtain a different result:
{
"problem_details": {
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "Bad Request",
"status": 400,
"detail": "A file with the same name already exists."
},
"content_type": "application/problem+json",
"status_code": 400
}
If we want the response headers, we try something like this:
curl -u "YOUR TEST API KEY (starts with ik_test_)": \
-i -F "file=@path/to/file/filename.xml" \
https://api.invoicetronic.com/v1/send/file
We added the -i
option to get the response headers, but we had to let go of the jq pipe since we're not getting a valid JSON back anymore, and jq would fail:
HTTP/2 400
content-type: application/json; charset=utf-8
date: Thu, 23 Jan 2025 16:13:55 GMT
...
{"problem_details":{"type":"https://tools.ietf.org/html/rfc9110#section-15.5.1","title":"Bad Request","status":400,"detail":"A file with the same name already exists."},"content_type":"application/problem+json","status_code":400}
Finally, how to save the response to a file? With curl's -o
option:
curl -u "YOUR TEST API KEY (starts with ik_test_)": \
-o response.json -F "file=@path/to/file/filename.xml" \
https://api.invoicetronic.com/v1/send/file
In the above example, the result goes to response.json instead of the console.
Pre-flight validation
Pre-flight validation is requested with the validate
query parameter:
curl -u "YOUR TEST API KEY (starts with ik_test_)": \
-o response.json -F "file=@path/to/file/filename.xml" \
https://api.invoicetronic.com/v1/send/file?validate=true
Should validation fail, a 400 Bad Request
response will be returned to the client, with validation issues listed within its body. If validation successes, the invoice will be stored and queried for SDI submission, and a 200 OK
response will be sent back to the client. As you would expect, the validate
parameter is available on all POST endpoints: xml, json, file, and entity.
Digital signatures
We want to upload an invoice and apply a digital signature to it.
curl -u "YOUR TEST API KEY (starts with ik_test_)": \
-o response.json -F "file=@path/to/file/filename.xml" \
https://api.invoicetronic.com/v1/send/file?signature=Apply
Digital signatures are controlled by the signature
query parameter. By adding signature=Apply
to the URL above, we requested a digital signature to be applied on the invoice. Once the signature is applied, the filename is updated accordingly (filename.xml becomes filename.xml.p7m). Keep in mind, if the submitted document is signed already, no operation will be performed to preserve the original signature.
In the same vein, signature=None
prevents any signature to be applied on a document. signature=None
wins even on unsigned FPA12 type documents (public authority offices) which would otherwise be signed by default, as signatures are mandatory on them.
The response body will be updated to reflect changes occured to the document. Consider the example request above. We submitted a plain XML file, requesting it to be digitally signed before it is sent to the SDI. A typical API response to a request like that would look like this:
{
...
"file_name": "filename.xml.p7m",
"format": "FPR12",
"payload": "MII...",
...
"encoding": "Base64",
"id": 225,
"created": "2025-01-23T16:55:51.491271Z",
}
The p7m extension has been added to file_name
. Because its content is now digitally signed, payload
is Base64 encoded, and the encoding
property reflects that.
Receive
We want to download unread invoices from the SDI. This is how we achieve that with curl:
curl -u "YOUR TEST API KEY (starts with ik_test_)": \
"https://api.invoicetronic.com/v1/receive?unread=true" | jq
Make sure you plug in 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.
A few things of note:
- We add a colon following the API key. It prevents curl from asking for the password. If you forget the colon, curl will go interactive and ask for a password: leave it blank.
- We're adding
?unread=true
to our query because we only want invoices we haven't downloaded before. - Because we're adding a query argument (and don't want to deal with escape characters), we must enclose the whole query within double quotes.
- The
| jq
pipe is is not strictly required, but it will output a pleasantly readable JSON. You might want to leave it out in a script.
Thanks to the jq pipe we obtain a JSON response like this one:
[
{
"is_read": false,
"message_id": "234234234",
"user_id": 1,
"company_id": 1,
"committente": "IT01180680397",
"prestatore": "IT06157670966",
"identifier": "234234",
"file_name": "filename.xml",
"format": "FPR12",
"payload": "PD94bWwgdmVyc2lvbj0i...",
"last_update": "2025-01-21T15:39:55.734848Z",
"date_sent": null,
"documents": [
{
"number": "123",
"date": "2024-05-31T22:00:00Z"
}
],
"encoding": "Base64",
"id": 48,
"created": "2025-01-21T15:39:28.57467Z",
"version": 1781881
}
]
In the example above, we received a list of just one document. If we resend the same request, however, we get a different result:
We get an empty list because we requested unread documents, and now there are none (we read the last fresh one moments ago).
If we want the response headers too, we try something like this:
curl -i -u "YOUR TEST API KEY (starts with ik_test_)": \
"https://api.invoicetronic.com/v1/receive?unread=true"
We added the -i
option to get the response headers, but we had to let go of the jq pipe since we're not getting a valid JSON back anymore:
Finally, how to save the response to a file? With curl's -o
option:
curl -i -o response.json -u "YOUR TEST API KEY (starts with ik_test_)": \
"https://api.invoicetronic.com/v1/receive?unread=true"
In the above example, the result goes to response.json instead of the console.
Not receiving invoices in the live environment?
Ensure your correspondents use 7HD37X0 as the value of their invoices' Codice Destinatario
field. That is how SDI knows they should be forwarded to the Invoicetronic API.
What we learned
We learned how to use curl to send and receive invoices. curl is everywhere, does not require us to use a programming language or SDK and is scriptable. It is very powerful and allows you to perform various tasks, but it can be rather complex to master with all its many options, whistles and bells.
If all you are looking for is to send and receive invoices from the command line, you may want to look at our invoice
tool.