- Quick Start
- Invoice
Quick Start
Invoice
Introduction
The objective of this guide is to take you through the process of generating an invoice you can send to your customers. Invopop is very flexible, so for this example we assuming the following conditions:
-
We’re in Spain, although the steps are essentially the same anywhere.
-
We have the basic information about the invoice: date, supplier, customer, items, and the tax type to apply to each.
-
Our invoices do not already have a sequential code, but we want one that looks like
TEST2201001
. -
Customers require an Electronic invoice alongside a PDF.
-
We want an email copy of our invoice.
You don’t need to be a developer to use this guide, but you will need to have a bit of experience using a command terminal and know how to create a text file. For sending requests to the server we use the “curl” command, included with most operating systems. You might however find it easier to use a visual tool for sending HTTP requests like Postman.
We assume here that you’ve already followed the steps in the Authentication guide, and have an access token ready to use.
To give you a heads up, these are the stages we’ll be going through:
-
Create a new series
-
Define a set of integrations
-
Build a new workflow that uses the integrations
-
Upload a document
-
Create a Job
-
Extract the results
Let’s get started.
Define a Series
Most tax jurisdictions require invoices to contain a sequential or consecutive code that allows inspectors to visually confirm the order of your invoices and check for any gaps. Some countries take this very seriously, and may impose penalities if not done properly.
Generating sequential numbers reliably, especially in a distributed system, is actually quite a tough technical challenge. Fortunately at Invopop we’ve tried to hide away the complexity with a simple interface.
Inside the Invopop Console:
-
Enter the Series section on the side menu.
-
Tap the ”New Series” button.
-
In Name enter “Test Series”.
-
In Prefix enter “TEST22”.
-
In Padding, leave as
5
. -
In Start Number, enter
1000
. -
Notice that the Preview box shows
TEST2201000
, and tap the ”Save” button. -
The new series should now appear in the list.
In the next section, we’ll figure out how to use this new sequence inside an integration.
Create Integrations
An integration defines a single operation that should happen to a document sent to Invopop. On its own, an integration can’t do anything until it is added to a workflow and executed via a job, which we’ll go through in the following sections. First however we need to create integrations for the following three key objectives:
-
Assign a sequential code (series) to an invoice.
-
Generate an Electronic Invoice, for Spain we’ll use FacturaE XML format.
-
Generate a PDF.
-
Send the complete PDF and electronic invoice via email.
Each step is pretty simple, but we’ve split them into the following subsections for clarity.
Series
-
Enter the Integrations section.
-
Tap ”New Integration“.
-
From the list of providers, click Series Enumerator.
-
The Name should already be set to “Series Enumerator”.
-
Tap ”Edit configuration“.
-
In the popup dialog, pick the “Test Series” option representing series we created in the last section.
-
Check the “Close draft envelopes automatically after adding sequence code” checkbox.
-
Tap ”Save”, you should see the configuration field is filled in with some JSON.
-
Tap ”Save” to create the new integration.
The integration will now appear in the list.
FacturaE
From inside the Integrations section:
-
Tap ”New Integration“.
-
Select FacturaE from the list of providers.
-
Click ”Edit configuration“.
-
Read through and check the “Accept Legal Terms and Conditions” checkbox.
-
Tap ”Save“.
-
Tap ”Save” again to persist and close this new integration.
There should now be a FacturaE integration available in the list.
Again from the Integrations section:
-
Tap ”New Integration“.
-
Select PDF Generator from the list.
-
Tap ”Edit configuration“.
-
Choose a language for the invoices and upload a company logo if you have one to hand.
-
Tap ”Save“.
-
Click ”Save” again to store the PDF generator integration.
After that, there will be a PDF Generator integration alongside the others.
Finally, we need to configure sending an email from the Integrations section:
1.Tap ”New Integration“.
2.Select Send to Email.
-
Click ”Edit configuration“.
-
Introduce a destination in the “To:” or “BCC:” fields by typing the address either in regular or “mailbox” format. (mailbox format includes name, e.g.
John Smith <john.smith@example.com>
). -
Hit the enter key to add the address to the lists.
-
Optionally tap the “Automatically add invoice customers” checkbox if you’d like to send copies of emails to customers defined in invoices.
-
Tap ”Save“.
-
Tap ”Save” again to save this new email integration.
Having completed all these steps, you should now have 4 different integrations available to combine together into a single workflow.
Define the Workflow
A workflow in Invopop is essentially a list of integrations that will be executed one-by-one for an incoming job. You can define as many workflows as you need with different combinations of integrations. For this example we’re going to continue what we started for the invoicing scenario.
1.Head to the Workflows section.
2.Tap ”New Workflow.
-
Use the Name field to give this new workflow and easy way to be identified, like “Process Invoice”.
-
In the steps section, tap the ”+” button.
-
Tap the Series Enumerator integration.
-
It’ll be added to the list of steps.
-
Repeat steps 4. to 6. for the remaining integrations:
- FacturaE
- PDF Generator
- Send to Email
-
Tap the ”Save” button.
Congratulations! You just created a new workflow. Notice the ”ID:” field, this is the identifier we’ll need later to be able to use this workflow from the terminal or command line.
Upload a Document
We’ve prepared a workflow with a set of integrations to process and convert invoices. Our next step is to upload a document to be processed by the workflow and extract the results.
At Invopop we use GOBL as our base format. It’s been purpose built (by us) to make it easy to create business documents using JSON and contain everything needed to be globally compatible.
If you haven’t had chance to read the GOBL Docs, there are two basic concepts that are important to understand:
1.A GOBL Document represents a data structure containing a payload; an invoice, message, contact details, or some other unit of business data, defined using a JSON Schema that GOBL understands.
2.A GOBL Envelope is a wrapper around a GOBL Document that adds meta-data like a digest and digital signatures.
At Invopop, we only ever store documents which are contained inside a GOBL Envelope. This is important as it means we can verify the contents and ensure no changes have been made. Most interfaces and APIs in Invopop however will automatically wrap documents inside envelopes, so you don’t need to worry about it when uploading, but you will need to take this into account when downloading.
In the next steps we’re going to need a sample partial GOBL Invoice document to upload, here’s one we created earlier:
{
"$schema": "https://gobl.org/draft-0/bill/invoice",
"currency": "EUR",
"issue_date": "2022-03-29",
"supplier": {
"tax_id": {
"country": "ES",
"code": "B28774008"
},
"name": "Biz España S.L.",
"emails": [
{
"addr": "billing@bizspain.es"
}
],
"addresses": [
{
"num": "42",
"street": "Calle Pradillo",
"locality": "Madrid",
"region": "Madrid",
"code": "28002",
"country": "ES"
}
]
},
"customer": {
"tax_id": {
"country": "ES",
"code": "B33105842"
},
"name": "Customer Spain S.L.",
"emails": [
{
"addr": "sam@customer.com"
}
],
"addresses": [
{
"num": "10",
"street": "Calle Mayor",
"locality": "Madrid",
"region": "Madrid",
"code": "28003",
"country": "ES"
}
]
},
"lines": [
{
"quantity": 50,
"item": {
"name": "Promotional mug",
"price": "16.00"
},
"taxes": [
{
"cat": "VAT",
"rate": "standard"
}
]
}
]
}
The most important bits of data to take away from this document are the:
-
JSON schema that identifies the GOBL Invoice document type,
-
currency and issue date,
-
supplier and customer, with their “tax id”, and,
-
lines of items of a given quantity and price to which VAT at a “standard” rate needs to be applied.
Note we used the word partial earlier to describe this document. It’s not a complete and valid GOBL document as it doesn’t contain important details like the invoice code or tax totals. We call these draft documents and there is a specific property in the header of GOBL Envelopes that indicates we’re working with a draft. As we’ll see shortly, once uploaded to Invopop, the API and workflow we created earlier will automatically assign an invoice code and make the required calculations so it can be finalized.
For more details on creating Invoices, see the GOBL Documentation Site. You’ll find more guides there on all the different options and configurations available for invoices, including ways you can build complete GOBL Envelopes with signatures.
Let’s upload this document to Invopop:
1.Copy and paste the JSON above into you favorite text editor.
2.Save the JSON into a file named invoice.json
in a temporary folder you can
easily find from the command line (Downloads
is usually a safe bet).
-
Open the Terminal or command line, and enter the temporary directory, e.g.
cd ~/Downloads
. -
Upload the document using the following Curl command:
curl -H "Authorization: Bearer $INVOPOP_TOKEN" -X POST -F data=@invoice.json https://api.invopop.com/silo/v1/entries | jq .
The response back from the server will be similar to the following:
{
"id": "181ecd5a-f242-11ec-82f1-0242ac150010",
"created_at": "2022-06-22T15:43:42.814Z",
"updated_at": "2022-06-22T15:43:42.814Z",
"env_schema": "https://gobl.org/draft-0/envelope",
"doc_schema": "https://gobl.org/draft-0/bill/invoice",
"digest": {
"alg": "sha256",
"val": "ca93946485519b3aadf82de2f1654523c98f8afdf90da28ea1d0067a59d1341a"
},
"draft": true,
"data": {
"$schema": "https://gobl.org/draft-0/envelope",
"head": {
"uuid": "181ecd5a-f242-11ec-82f1-0242ac150010",
"dig": {
"alg": "sha256",
"val": "ca93946485519b3aadf82de2f1654523c98f8afdf90da28ea1d0067a59d1341a"
},
"draft": true
},
"doc": {
"$schema": "https://gobl.org/draft-0/bill/invoice",
"code": "",
"currency": "EUR",
"issue_date": "2022-03-29",
"supplier": {
"tax_id": {
"country": "ES",
"code": "B28774008"
},
"name": "Biz España S.L.",
"addresses": [
{
"num": "42",
"street": "Calle Pradillo",
"locality": "Madrid",
"region": "Madrid",
"code": "28002",
"country": "ES"
}
],
"emails": [
{
"addr": "billing@bizspain.es"
}
]
},
"customer": {
"tax_id": {
"country": "ES",
"code": "B33105842"
},
"name": "Customer Spain S.L.",
"addresses": [
{
"num": "10",
"street": "Calle Mayor",
"locality": "Madrid",
"region": "Madrid",
"code": "28003",
"country": "ES"
}
],
"emails": [
{
"addr": "sam@customer.com"
}
]
},
"lines": [
{
"i": 1,
"quantity": "50",
"item": {
"name": "Promotional mug",
"price": "16.00"
},
"sum": "800.00",
"taxes": [
{
"cat": "VAT",
"rate": "standard",
"percent": "21.0%"
}
],
"total": "800.00"
}
],
"totals": {
"sum": "800.00",
"total": "800.00",
"taxes": {
"categories": [
{
"code": "VAT",
"rates": [
{
"key": "standard",
"base": "800.00",
"percent": "21.0%",
"amount": "168.00"
}
],
"base": "800.00",
"amount": "168.00"
}
],
"sum": "168.00"
},
"tax": "168.00",
"total_with_tax": "968.00",
"payable": "968.00"
}
},
"sigs": []
}
}
Let’s go over some of the important points in that response:
-
everything is wrapped inside a response containing the data and copies of key fields like the schemas, digest, and draft status,
-
we’ve created an entry in the silo. “Silo” is the fancy name we gave to the place where you store your GOBL Envelopes,
-
there is an
id
property that uniquely identifies this entry in the silo, -
the
data
property includes a complete GOBL Envelope with the invoice, -
the invoice has now been completed with all the totals and tax calculations, and,
-
the invoice’s
code
field is still empty.
we don’t recommend making HTTP POST
calls like in this example as they do
not include an ID that would make the request
idempotent. This is fine for
testing, but in production environments, you should pre-assign a UUID (v1) to
the request and include it in the path in a PUT
call. This way, if for some
reason the request gets repeated, you’ll only have one copy.
Create a Job
Now for the exciting part. But first, let’s recap what we have:
-
A set of integrations to execute.
-
A workflow that defines which integrations to use, with an ID.
-
A silo entry ID generated in the last request when we uploaded the Invoice.
Let’s put all these together and execute a job. Here’s the command to perform from the terminal and don’t forget to update the IDs with your values that were generated for the workflow and silo entry:
curl -H "Authorization: Bearer $INVOPOP_TOKEN" -X POST -F workflow_id=0d9602ab-a2e2-413c-adf0-7abf4dd25c12 -F silo_entry_id=181ecd5a-f242-11ec-82f1-0242ac150010 "https://api.invopop.com/transform/v1/jobs?wait=30" | jq .
{
"id": "a42886a8-f23e-11ec-9be8-0242ac150010",
"created_at": "2022-06-22T15:18:59.770Z",
"updated_at": "2022-06-22T15:23:16.256Z",
"silo_entry_id": "181ecd5a-f242-11ec-82f1-0242ac150010",
"workflow_id": "0d9602ab-a2e2-413c-adf0-7abf4dd25c12",
"status": "OK",
"completed_at": "2022-06-22T15:23:16.256Z",
"intents": [
{
"id": "a979ec1a-cd78-4ad0-bae6-d22df4b246ac",
"created_at": "2022-06-22T15:18:59.821Z",
"updated_at": "2022-06-22T15:19:00.064Z",
"integration_id": "4497eab3-5a47-4e34-9196-3d1cce237aff",
"events": [
{
"index": 1,
"status": "RUN",
"at": "2022-06-22T15:18:59.832Z"
},
{
"index": 2,
"status": "OK",
"at": "2022-06-22T15:19:00.064Z"
}
],
"completed": true
},
{
"id": "483e20b0-4e9a-4fb3-bd76-9a1dabbc6c8f",
"created_at": "2022-06-22T15:19:00.076Z",
"updated_at": "2022-06-22T15:19:00.450Z",
"integration_id": "65018ad7-2cd9-4284-a5d5-974dd541f1f9",
"events": [
{
"index": 1,
"status": "RUN",
"at": "2022-06-22T15:19:00.094Z"
},
{
"index": 2,
"status": "OK",
"at": "2022-06-22T15:19:00.450Z"
}
],
"completed": true
},
{
"id": "655021e4-c941-4179-910b-5ba5e2ac439a",
"created_at": "2022-06-22T15:19:00.456Z",
"updated_at": "2022-06-22T15:19:01.054Z",
"integration_id": "2b16201f-6f38-4a0e-bd24-5e1e737b9305",
"events": [
{
"index": 1,
"status": "RUN",
"at": "2022-06-22T15:19:00.464Z"
},
{
"index": 2,
"status": "OK",
"at": "2022-06-22T15:19:01.053Z"
}
],
"completed": true
},
{
"id": "f5c90cb2-03f1-494e-8a4c-98e6c535824a",
"created_at": "2022-06-22T15:19:01.059Z",
"updated_at": "2022-06-22T15:23:16.250Z",
"integration_id": "b3440891-067f-47c4-b6b8-6afc97d0be86",
"events": [
{
"index": 1,
"status": "RUN",
"at": "2022-06-22T15:19:01.068Z"
},
{
"index": 2,
"status": "OK",
"at": "2022-06-22T15:19:01.083Z"
}
],
"completed": true
}
],
"envelope": {
"$schema": "https://gobl.org/draft-0/envelope",
"head": {
"uuid": "181ecd5a-f242-11ec-82f1-0242ac150010",
"dig": {
"alg": "sha256",
"val": "a9e2e745f095382db4814732d17ca936355a2e684371e5e9bcbbb6bfd24e61f4"
}
},
"doc": {
"$schema": "https://gobl.org/draft-0/bill/invoice",
"code": "INV0200105",
"currency": "EUR",
"issue_date": "2022-03-29",
"supplier": {
"tax_id": {
"country": "ES",
"code": "B28774008"
},
"name": "Biz España S.L.",
"addresses": [
{
"num": "42",
"street": "Calle Pradillo",
"locality": "Madrid",
"region": "Madrid",
"code": "28002",
"country": "ES"
}
],
"emails": [
{
"addr": "billing@bizspain.es"
}
]
},
"customer": {
"tax_id": {
"country": "ES",
"code": "B33105842"
},
"name": "Customer Spain S.L.",
"addresses": [
{
"num": "10",
"street": "Calle Mayor",
"locality": "Madrid",
"region": "Madrid",
"code": "28003",
"country": "ES"
}
],
"emails": [
{
"addr": "sam@customer.com"
}
]
},
"lines": [
{
"i": 1,
"quantity": "50",
"item": {
"name": "Promotional mug",
"price": "16.00"
},
"sum": "800.00",
"taxes": [
{
"cat": "VAT",
"rate": "standard",
"percent": "21.0%"
}
],
"total": "800.00"
}
],
"totals": {
"sum": "800.00",
"total": "800.00",
"taxes": {
"categories": [
{
"code": "VAT",
"rates": [
{
"key": "standard",
"base": "800.00",
"percent": "21.0%",
"amount": "168.00"
}
],
"base": "800.00",
"amount": "168.00"
}
],
"sum": "168.00"
},
"tax": "168.00",
"total_with_tax": "968.00",
"payable": "968.00"
}
},
"sigs": [
"eyJhbGciOiJFUzI1NiIsImtpZCI6Ijk1Nzg1MDY1LWJkN2YtNGY4MC1iYzc4LTQ3NTI2ZmZiODcxYiJ9.eyJ1dWlkIjoiZWMwNDkzODYtZjIyMC0xMWVjLWJlOGYtMDI0MmFjMTUwMDEwIiwiZGlnIjp7ImFsZyI6InNoYTI1NiIsInZhbCI6ImE5ZTJlNzQ1ZjA5NTM4MmRiNDgxNDczMmQxN2NhOTM2MzU1YTJlNjg0MzcxZTVlOWJjYmJiNmJmZDI0ZTYxZjQifX0.dexgs6CRQG-GgN-5X4hINOPoZFv0IhUH4XIsX08YztT2j-ndL9R3je7BjydNot9yVKd8BRsnDK-4Xe4JIr2KDA"
]
},
"attachments": [
{
"id": "483e20b0-4e9a-4fb3-bd76-9a1dabbc6c8f",
"name": "facturae-invoice.xml",
"hash": "70ffaede94d3466dbf23ddc8e0f69476de54106606f9f30938f9f44526600a8e",
"mime": "text/xml; charset=utf-8",
"size": 10941,
"url": "https://silo.invopop.com/7ASThvIgEey-jwJCrBUAEA/SD4gsE6aT7O9dpodq7xsjw/facturae-invoice.xml?h=70ffaede94d3466d"
},
{
"id": "655021e4-c941-4179-910b-5ba5e2ac439a",
"name": "invoice.pdf",
"hash": "fb9817cb640c2e0e26426fb3e34b868e64acafe1aab193a6c4ca109fbd43e230",
"mime": "application/pdf",
"size": 70898,
"url": "https://silo.invopop.com/7ASThvIgEey-jwJCrBUAEA/ZVAh5MlBQXmRC1ul4qxDmg/invoice.pdf?h=fb9817cb640c2e0e"
}
]
}
There is a lot of JSON data there, but let’s break it down into the key bits of data:
-
Everything is wrapped inside a “Job” object with the key fields like
workflow_id
andsilo_entry_id
. -
Job
status
showsOK
indicating that this job has been completed successfully. -
The
intents
list describes all the steps the job has gone through for each of the connectors defined in the workflow. -
envelope
contains a copy of the invoice we uploaded earlier except it has now been assigned a code, and is no longer a draft. -
attachments
includes a list of links to all the files that have been generated for the envelope: an XML FacturaE document, and a single pdf.
Download the Results
Having completed a job, we can view the results in the
Invopop Console or download the results directly
based on the previous response. Take a look at the attachments
attribute. Each
attachment has a URL attribute that we can use to download the contents and then
open the file:
curl "https://silo.invopop.com/7ASThvIgEey-jwJCrBUAEA/ZVAh5MlBQXmRC1ul4qxDmg/invoice.pdf?h=fb9817cb640c2e0e" > invoice.pdf
open invoice.pdf
You can also download the attachment directly in your browser by copying and pasting the URL.