Skip to main content

Notifications

The webhooks are a pattern to deliver notifications between applications via HTTP endpoints, allowing external applications to register an HTTP endpoint to which notifications are delivered.

After a Subscription has been established, notifications will start being sent to the URL you specified; on this page, we'll explain what's the format and how to manage them correctly.

โ˜๏ธย  CloudEventsโ€‹

Our implementation is loosely compliant with the CloudEvents Specification.

You don't need to know the specification in detail to use our webhooks, but if you're curious you can check the dedicated page.

๐ŸŽฏย  Update the Targetโ€‹

If you were able to create and verify your Subscription, this means that you already have a target and that you managed the GET request correctly.

The Notifications events will be sent to the same endpoint, but in this case, you'll be dealing with POST requests. Below you can find the structure of the notification and how to manage it.

๐Ÿ“ฎย  The Notificationโ€‹

A Notification is an HTTP POST request sent to the Target's endpoint by our Webhooks system; each notification is compliant with the CloudEvents Core specification and with the HTTP Protocol Binding (Binary and Structured Content Modes).

Our Webhooks will handle a certain number of Notification Types, that can be categorized as follows:

  • Verification Notifications
  • Welcome Notifications
  • Event Notifications

The following structure will be applied to the Welcome and Event Notifications, while the Verification Notifications are explained in the Subscriptions page.

During the subscription process you can specify if the notifications you will receive will be in the Binary or Structured content mode, below you can find examples of both the options:

curl --location --request POST 'https://example.com/notifications' \
--header 'User-Agent: FattureInCloud/API-WEBHOOK' \
--header 'ce-type: it.fattureincloud.webhooks.entities.clients.create' \
--header 'ce-time: 2023-04-04T12:54:21+02:00' \
--header 'ce-subject: company:108061' \
--header 'ce-specversion: 1.0' \
--header 'ce-source: https://api-v2.fattureincloud.it' \
--header 'ce-id: 198:f059b211-24f4-44ab-9859-b1613a9a0712' \
--header 'Authorization: Bearer SIGNATURE' \
--header 'Connection: close' \
--header 'Content-Type: application/json' \
--data-raw '{
"data": {
"ids": [
3062300
]
}
}'

The CloudEvents Attributes are inserted as HTTP Headers prefixed by ce- in the Binary Mode, while in the Structured Mode they are available in the request body, the following list explains their values:

  • type: the Type of the Notification; it identifies the action that was performed on the resource
  • time: the timestamp of when the occurrence happened, in RFC 3339 format
  • subject: the subject of the event, for now, the subject will always be the company of the resource; it has the format "TYPE:ID" where TYPE is "company" and ID is the Company ID
  • specversion: the version of the CloudEvents specification that the event uses (at this moment "1.0")
  • source: the context in which an event happened; it has value "https://api-v2.fattureincloud.it"
  • id: a unique string identifier for the notification

There are some additional Headers:

  • Authorization: it will contain a token that should be used to verify that the notification was issued by our system; see the dedicated section for more info
  • Connection: it is set as "close" and indicates that our system will close the HTTP connection after the completion of the response
  • User-Agent: even if the User-Agent can't be trusted since it can be set to an arbitrary value, it is used to indicate that the sender is the Fatture in Cloud Webhooks System

The Body of the Notification will contain a simple JSON object, where the data.ids field will contain the array of the resources' IDs modified by this event; most of the time it will contain only a single ID, but a single event might modify more resources (for example, in an import of multiple clients via Excel file) and in this case the array could contain more than one identifier.

As you can see, the body won't include any possibly sensitive information about the resources, to avoid any possible data leak due to the misuse of our Webhooks. We expect your application to be able to retrieve the updated status of the resource using the related Get API method passing the resource ID as a parameter; the API methods are authenticated, so it is required to possess the permissions on the resource to perform the expected operations.

๐Ÿšดย  How should I use it?โ€‹

The behavior that your application must adopt when a notification is received is up to you; the notification's objective is to notify you that something has happened and that you could need to do something to react to the event, but we don't know the scope of your application.

Even in this scenario, we still require you to respect some simple rules that are necessary to provide the best performance to all the webhooks users; if you don't respect these rules your subscriptions will most probably be deactivated, so make sure to read it carefully!

๐ŸŽย  Fast is better!โ€‹

First, you should answer fast to our request. Our Notification system has a short timeout time: if your application will answer too slowly, it will be handled as an error on your notification, and it will be managed accordingly.

Since it's impossible to predict how fast your system will complete, you should process the notification asynchronously. For example, you could insert the notification in a queue, use some library to create background jobs, or you could simply store the notification in a dedicated database table and process the records periodically using a cron job.

In the Additional Resources you can find a list of links that you could use to manage your applications properly.

๐Ÿ›กย  Defend yourself!โ€‹

When you try to create a new subscription, we check if the URL you exposed is using HTTPS. We won't accept your subscription if you're using HTTP.

Also, to avoid possible data leaks we don't share sensitive information in the notification body, but just the identifier of the resources involved in the event. To access the actual state of the resource you'll need to perform an API call after you received the notification.

๐Ÿ“คย  What should I answer?โ€‹

If our system obtains a Success Status from your target endpoint, the message will be considered as successfully delivered and we will not retry to send it.

The list of the Success Statuses can be found below:

HTTP Status CodeHTTP StatusMeaning
200OKThe request has succeeded.
201CreatedThe request has succeeded and led to the creation of a resource.
202AcceptedThe request has been accepted for processing, but the processing has not been completed.
204No ContentThe request has succeeded, but the client doesn't need to navigate away from its current page.
102ProcessingThe request has been received and the server is working on it.

If our system will receive an Error status, the adopted behavior depends on the specific status code that was returned.

If we receive a Retryable Error Status, we will try again to send the notification for a total of four attempts, with a growing interval between a try and the next one. If a notification receives a Retryable Error even at the fourth attempt, it will be discarded and no further delivery attempts will be performed for that same notification.

We consider all the Server Error Responses (e.g. the 500-599 status codes) as Retryable errors.

Every status not included in the lists above will be considered an Unretryable Error Status. In this case, the Notification will be immediately discarded and our system will not try to deliver it again; this is valid even for the redelivery attempts, so if for example the second attempt fails with an Unretryable Error there won't be a third attempt. Please, note that we'll manage also the Redirection Messages (e.g. the 300-399 status codes) as errors.

A special exception is the 410 Gone status code: in this case, we consider that your application was permanently dismissed or that it wants to get rid of the active subscription; if we receive this kind of error we'll immediately remove the subscription as explained in the Subscriptions page.

โ™Šย  Beware of the twins!โ€‹

Our system adopts an At-Least-Once Delivery Policy, this means that the Notifications will be sent at least once, but they could also occasionally be delivered more times even if the first delivery was successful.

If this could be an issue for your application, we suggest you use the Notification ID to filter out the twin messages and avoid reprocessing them a second time.

๐Ÿกย  What about the order?โ€‹

Our system will deliver the events in generation order most of the time. In some rare cases, an event could break the order and be delivered after another one that was generated later; for example, if it is necessary to redeliver a message after a retryable error.

If needed, you can use the timestamp included in the CloudEvents attributes to find out when an event occurred and adopt the correct strategy.

๐Ÿ‘ฎย  Validate the requests!โ€‹

Your Target endpoint unfortunately will not be protected by an authentication mechanism, so it could be potentially abused by a malicious third party.

If you want to be sure to protect your application from potential attacks, you can use the token included in the Authorization header to verify if the message was issued by Fatture in Cloud.

The header value will contain (after the "Bearer" prefix) a JWT token that you will be able to verify by using the Public Key published on this page.

The token will be signed by using a Private Key used only for this purpose and not shared externally, so you will be sure that if the token is valid the request was legit.

The Public Key, Base 64 encoded, can be found here:

LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFL1JvSElqZ1k3aGZYZlk1cC9KeStLL0ZndU1aNAozVHZaOXQ0ZU43K2t4UTBNSnpLdG93djRDY1lURnFyQm03aE1CNVpXS25xTHoyNEQ2bFFqU0wwWXN3PT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==

The JWT's payload will contain the following claims:

Claim KeyClaim NameMeaning
jtiJWT IDThe ID of the JWT (the same ID used for the message in the CloudEvents Attributes)
issIssuerWho generated the JWT ("https://api-v2.fattureincloud.it")
expExpiration timeWhen the JWT will expire (3 hours from now)
subSubjectThe subject, it can also be found in the CloudEvents Attributes
audAudienceA list containing the Target's Endpoint
iatIssued atThe timestamp of the moment when the token was generated
aidApplication IDThe Fatture in Cloud's Application ID related to the Subscription

After you verified the token, you can check the claims to see if they match the message content.

You can verify the JWT token by using the code below (replace the Public Key with the one provided above):

// https://www.nuget.org/packages/JWT

using System;
using System.Security.Cryptography;
using Jose;

namespace Application
{
class Program
{
static void Main(string[] args)
{
Console.Write(VerifyAuthToken("TOKEN"));
}

static bool VerifyAuthToken(string token)
{
try
{
var pub = Convert.FromBase64String("FIC_PUBLIC_KEY");

var key = ECDsa.Create();
key.ImportFromPem(System.Text.Encoding.Default.GetString(pub));

JWT.Decode(token, key, JwsAlgorithm.ES256);
}
catch (Exception e)
{
return false;
}

return true;
}
}
}

โณย  Subscriptions expire!โ€‹

To avoid sending notifications to faulty or dismissed systems, we implemented an Expiration Mechanism on our Subscriptions: if a Subscription expires, our system will stop sending Notifications and you'll need to create (and verify) a new Subscription if you're still interested in being synchronized.

Expiration is triggered when our system retrieves an error from the Target system, so you will not have problems if you'll implement your service properly. Check the related page to learn more about it.

๐Ÿ“šย  Additional Resourcesโ€‹

Below you can find a non-exhaustive list of tools you can use to manage the notifications asynchronously:

โœ‰๏ธย  Standalone servicesโ€‹

๐Ÿ› ย  Background Librariesโ€‹