Subscriptions
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.
This means that the first step to being able to use this functionality is to Subscribe to the Service. On this page the Subscription process is explained, guiding you on requesting notifications for Fatture in Cloud events.
The Webhooks are still in a Closed Beta version, this means that you can start using it only if we explicitly authorized you. If you try to create a subscription without prior authorization, your requests will always result in an error.
If you are interested in helping us test this functionality, you can discover how to apply to pur Beta phase on the Webhooks Enablement page.
☁️ 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.
🎯 Prepare the Target
Our notifications must be sent to your system, so you should tell us where we have to send the messages; to do it, you'll need to prepare a Target and specify its address while creating a new subscription.
We suggest you prepare the target beforehand, and only then create the subscription. This is because the subscription process includes a Verification Phase, and this requires the Target to send a proper response.
The Target is a simple HTTP endpoint, that must be exposed by your application and must be able to accept REST calls; specifically, it must be designed in a way that makes it able to accept GET and POST requests.
As you can imagine, our Webhooks will handle a certain number of Notification Types, that can be categorized as follows:
- Verification Notifications
- Welcome Notifications
- Event Notifications
Where the third one is a set of many notification types.
In this phase, to complete the Subscription it is enough to be able to manage correctly the Verification Notifications; the correct way to answer to the Verification Notification will be explained below.
For now, it is enough to answer with a 200 OK to the other Notification Events; the proper way to manage this kind of request will be explained on the Notifications page.
We expect you to expose your target using HTTPS, so we will refuse HTTP URLs when trying to create a Subscription.
In the next paragraphs we'll suppose that our target will be exposed at the following endpoint:
https://example.com/notifications
🗒 Subscribe
First, we need to require a new Subscription. To do so, we need to call the Create Subscription method like follows:
- cURL
- HTTP
curl --location --request POST 'https://api-v2.fattureincloud.it/c/company_id/subscriptions' \
--header 'Authorization: Bearer ACCESS_TOKEN' \
--header 'Content-Type: application/json' \
--data-raw '{
"data": {
"sink": "https://example.com/notifications",
"types": [
"it.fattureincloud.webhooks.entities.all.delete",
"it.fattureincloud.webhooks.issued_documents.all.update",
"it.fattureincloud.webhooks.received_documents.create"
],
"config": {
"mapping": "binary"
}
}
}
'
POST /c/company_id/subscriptions HTTP/1.1
Host: api-v2.fattureincloud.it
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json
Content-Length: 269
{
"data": {
"sink": "https://example.com/notifications",
"types": [
"it.fattureincloud.webhooks.entities.all.delete",
"it.fattureincloud.webhooks.issued_documents.all.update",
"it.fattureincloud.webhooks.received_documents.create"
],
"config": {
"mapping": "binary"
}
}
}
The corresponding code with our SDKs:
- C#
- Go
- Java
- JavaScript
- PHP
- Python
- Ruby
- TypeScript
using System.Collections.Generic;
using System.Diagnostics;
using It.FattureInCloud.Sdk.Api;
using It.FattureInCloud.Sdk.Client;
using It.FattureInCloud.Sdk.Model;
namespace Example
{
public class CreateWebhooksSubscriptionExample
{
public static void Main()
{
Configuration config = new Configuration();
// Configure OAuth2 access token for authorization: OAuth2AuthenticationCodeFlow
config.AccessToken = "YOUR_ACCESS_TOKEN";
var apiInstance = new WebhooksApi(config);
var companyId = 12345; // int | The ID of the company.
var createWebhooksSubscriptionRequest = new CreateWebhooksSubscriptionRequest(); // CreateWebhooksSubscriptionRequest | (optional)
try
{
// Create a Webhook Subscription
CreateWebhooksSubscriptionResponse result = apiInstance.CreateWebhooksSubscription(companyId, createWebhooksSubscriptionRequest);
Console.WriteLine(result);
}
catch (ApiException e)
{
Console.WriteLine("Exception when calling WebhooksApi.CreateWebhooksSubscription: " + e.Message);
Console.WriteLine("Status Code: " + e.ErrorCode);
Console.WriteLine(e.StackTrace);
}
}
}
}
package main
import (
"context"
"encoding/json"
"fmt"
"os"
fattureincloudapi "github.com/fattureincloud/fattureincloud-go-sdk/v2/api"
fattureincloud "github.com/fattureincloud/fattureincloud-go-sdk/v2/model"
)
func main() {
companyId := int32(12345) // int32 | The ID of the company.
createWebhooksSubscriptionRequest := *fattureincloud.NewCreateWebhooksSubscriptionRequest() // CreateWebhooksSubscriptionRequest | (optional)
auth := context.WithValue(context.Background(), fattureincloudapi.ContextAccessToken, "ACCESS_TOKEN")
configuration := fattureincloudapi.NewConfiguration()
apiClient := fattureincloudapi.NewAPIClient(configuration)
resp, r, err := apiClient.WebhooksAPI.CreateWebhooksSubscription(auth, companyId).CreateWebhooksSubscriptionRequest(createWebhooksSubscriptionRequest).Execute()
if err != nil {
fmt.Fprintf(os.Stderr, "Error when calling `WebhooksAPI.CreateWebhooksSubscription``: %v\n", err)
fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r)
}
// response from `CreateWebhooksSubscription`: CreateWebhooksSubscriptionResponse
json.NewEncoder(os.Stdout).Encode(resp)
}
// Import classes:
import it.fattureincloud.sdk.ApiClient;
import it.fattureincloud.sdk.ApiException;
import it.fattureincloud.sdk.Configuration;
import it.fattureincloud.sdk.auth.*;
import it.fattureincloud.sdk.models.*;
import it.fattureincloud.sdk.api.WebhooksApi;
public class Example {
public static void main(String[] args) {
ApiClient defaultClient = Configuration.getDefaultApiClient();
// Configure OAuth2 access token for authorization: OAuth2AuthenticationCodeFlow
OAuth OAuth2AuthenticationCodeFlow = (OAuth) defaultClient.getAuthentication("OAuth2AuthenticationCodeFlow");
OAuth2AuthenticationCodeFlow.setAccessToken("YOUR ACCESS TOKEN");
WebhooksApi apiInstance = new WebhooksApi(defaultClient);
Integer companyId = 12345; // Integer | The ID of the company.
CreateWebhooksSubscriptionRequest createWebhooksSubscriptionRequest = new CreateWebhooksSubscriptionRequest(); // CreateWebhooksSubscriptionRequest |
try {
CreateWebhooksSubscriptionResponse result = apiInstance.createWebhooksSubscription(companyId, createWebhooksSubscriptionRequest);
System.out.println(result);
} catch (ApiException e) {
System.err.println("Exception when calling WebhooksApi#createWebhooksSubscription");
System.err.println("Status code: " + e.getCode());
System.err.println("Reason: " + e.getResponseBody());
System.err.println("Response headers: " + e.getResponseHeaders());
e.printStackTrace();
}
}
}
import fattureInCloudSdk from '@fattureincloud/fattureincloud-js-sdk';
let defaultClient = fattureInCloudSdk.ApiClient.instance;
// Configure OAuth2 access token for authorization: OAuth2AuthenticationCodeFlow
let OAuth2AuthenticationCodeFlow = defaultClient.authentications['OAuth2AuthenticationCodeFlow'];
OAuth2AuthenticationCodeFlow.accessToken = 'YOUR ACCESS TOKEN';
let apiInstance = new fattureInCloudSdk.WebhooksApi();
let companyId = 12345; // Number | The ID of the company.
let opts = {
'createWebhooksSubscriptionRequest': {"data":{"sink":"http://www.test.com","types":["it.fattureincloud.webhooks.entities.create","it.fattureincloud.webhooks.issued_documents.create"]}} // CreateWebhooksSubscriptionRequest |
};
apiInstance.createWebhooksSubscription(companyId, opts).then((result) => {
console.log('API called successfully. Returned result: ' + JSON.stringify(result));
}, (error) => {
console.error(error);
});
<?php
require_once(__DIR__ . '/vendor/autoload.php');
// Configure OAuth2 access token for authorization: OAuth2AuthenticationCodeFlow
$config = FattureInCloud\Configuration::getDefaultConfiguration()->setAccessToken('YOUR_ACCESS_TOKEN');
$apiInstance = new FattureInCloud\Api\WebhooksApi(
// If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`.
// This is optional, `GuzzleHttp\Client` will be used as default.
new GuzzleHttp\Client(),
$config
);
$company_id = 12345; // int | The ID of the company.
$create_webhooks_subscription_request = new \FattureInCloud\Model\CreateWebhooksSubscriptionRequest; // \FattureInCloud\Model\CreateWebhooksSubscriptionRequest |
try {
$result = $apiInstance->createWebhooksSubscription($company_id, $create_webhooks_subscription_request);
print_r($result);
} catch (Exception $e) {
echo 'Exception when calling WebhooksApi->createWebhooksSubscription: ', $e->getMessage(), PHP_EOL;
}
import fattureincloud_python_sdk
from fattureincloud_python_sdk.models.create_webhooks_subscription_request import CreateWebhooksSubscriptionRequest
from fattureincloud_python_sdk.models.create_webhooks_subscription_response import CreateWebhooksSubscriptionResponse
from fattureincloud_python_sdk.rest import ApiException
from pprint import pprint
# Defining the host is optional and defaults to https://api-v2.fattureincloud.it
# See configuration.py for a list of all supported configuration parameters.
configuration = fattureincloud_python_sdk.Configuration()
# The client must configure the authentication and authorization parameters
# in accordance with the API server security policy.
# Examples for each auth method are provided below, use the example that
# satisfies your auth use case.
configuration.access_token = os.environ["ACCESS_TOKEN"]
# Enter a context with an instance of the API client
with fattureincloud_python_sdk.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = fattureincloud_python_sdk.WebhooksApi(api_client)
company_id = 12345 # int | The ID of the company.
create_webhooks_subscription_request = {"data":{"sink":"http://www.test.com","types":["it.fattureincloud.webhooks.entities.create","it.fattureincloud.webhooks.issued_documents.create"]}} # CreateWebhooksSubscriptionRequest | (optional)
try:
# Create a Webhook Subscription
api_response = api_instance.create_webhooks_subscription(company_id, create_webhooks_subscription_request=create_webhooks_subscription_request)
print("The response of WebhooksApi->create_webhooks_subscription:\n")
pprint(api_response)
except Exception as e:
print("Exception when calling WebhooksApi->create_webhooks_subscription: %s\n" % e)
require 'time'
require 'fattureincloud_ruby_sdk'
# setup authorization
FattureInCloud_Ruby_Sdk.configure do |config|
# Configure OAuth2 access token for authorization: OAuth2AuthenticationCodeFlow
config.access_token = 'YOUR ACCESS TOKEN'
end
api_instance = FattureInCloud_Ruby_Sdk::WebhooksApi.new
company_id = 12345 # Integer | The ID of the company.
opts = {
create_webhooks_subscription_request: FattureInCloud_Ruby_Sdk::CreateWebhooksSubscriptionRequest.new # CreateWebhooksSubscriptionRequest |
}
begin
# Create a Webhook Subscription
result = api_instance.create_webhooks_subscription(company_id, opts)
p result
rescue FattureInCloud_Ruby_Sdk::ApiError => e
puts "Error when calling WebhooksApi->create_webhooks_subscription: #{e}"
end
import { Configuration, WebhooksApi , CreateWebhooksSubscriptionRequest } from '@fattureincloud/fattureincloud-ts-sdk';
// Configure OAuth2 access token for authorization:
const apiConfig = new Configuration({
accessToken: "YOUR ACCESS TOKEN"
});
let apiInstance = new WebhooksApi(apiConfig);
let companyId = 12345; // Number | The ID of the company.
let createWebhooksSubscriptionRequest: CreateWebhooksSubscriptionRequest = {"data":{"sink":"http://www.test.com","types":["it.fattureincloud.webhooks.entities.create","it.fattureincloud.webhooks.issued_documents.create"]}} // CreateWebhooksSubscriptionRequest |
apiInstance.createWebhooksSubscription(companyId, createWebhooksSubscriptionRequest).then((data) => {
console.log('API called successfully. Returned data: ' + data);
}, (error) => {
console.error(error);
});
In the example, it is important to replace the Company ID with the ID of the Company owner of the resources we want to follow. For example, if I created a Fatture in Cloud App using my company 12345, and I want to be notified about the resources of the company 67890, the Company ID will be 67890 and not my own.
The body will contain the following params:
- data.sink: The HTTPS URL where the target is exposed
- data.types: An array of the event types the subscriber is interested in receiving
- data.config.mapping: The structure of the event you will receive, accepted values are binary (default) and structured, you can learn about the differences here
The request will be accepted only if the Access Token used for the request is associated with the company for the scopes related to the types inserted in the request.
The response provided by the Fatture in Cloud API will look something similar to this:
{
"data": {
"id": "SUB230",
"sink": "https://example.com/notifications",
"verified": true,
"types": [
"it.fattureincloud.webhooks.entities.suppliers.delete",
"it.fattureincloud.webhooks.issued_documents.invoices.update",
"it.fattureincloud.webhooks.issued_documents.quotes.update",
"it.fattureincloud.webhooks.issued_documents.proformas.update",
"it.fattureincloud.webhooks.issued_documents.receipts.update",
"it.fattureincloud.webhooks.issued_documents.delivery_notes.update",
"it.fattureincloud.webhooks.issued_documents.credit_notes.update",
"it.fattureincloud.webhooks.issued_documents.orders.update",
"it.fattureincloud.webhooks.issued_documents.work_reports.update",
"it.fattureincloud.webhooks.issued_documents.supplier_orders.update",
"it.fattureincloud.webhooks.issued_documents.self_invoices.update",
"it.fattureincloud.webhooks.received_documents.create"
],
"config": {
"mapping": "binary"
}
},
"warnings": [
"The 'it.fattureincloud.webhooks.entities.clients.delete' event is already registered for this application"
]
}
There are two additional fields:
- data.id: The ID of the subscription, that must be used for the other methods of the Subscriptions API; the ID will be a string, where the first part is the prefix "SUB" and the second part is an incremental number
- warnings: This indicates that the creation of the subscription failed for some of the message types
You can have warnings in these cases:
- If an app requested a message type for a company, and it already exists another subscription linked to the same message type for the same company. Only one subscription can be created for the couple app-type, so a duplicate will be rejected
- If the type does not exist
The subscription will still be created for all the valid message types left.
Please, note that the message types are different from the request: the message types list includes some Group Types, that can be used to select all the related events with one single entry; the final subscription will still contain the actual types, because the group types will be exploded at creation time. Check the event types page to find out more about the group types.
The newly created Subscription will be Unverified until when the Verification step will succeed; this means that the target will not receive the expected events until when the Subscription will be verified.
The Webhooks are still in a Closed Beta version, this means that you can start using it only if we explicitly authorized you.
If you are receiving the following error, it means that you're not enabled on the webhooks yet, and you must send a request as explained above.
{
"error": {
"message": "This app is not enabled to register webhooks",
"code": "NO_PERMISSION"
}
}
✅ Verify the subscription
Once the Subscription has been created, a new Verification Notification will be delivered to the Target to perform the Verification step: the Verification Notification will be sent as a REST GET call, and the target must be able to respond adequately.
This verification step is extremely important for Abuse Protection because it makes it possible for our systems to verify that your endpoint is expecting our notifications and that your endpoint was not registered by some malicious actor.
Below you can find an example of the Verification Notification, sent by our system to your endpoint:
- cURL
- HTTP
curl --location --request GET 'https://example.com/notifications' \
--header 'User-Agent: FattureInCloud/API-WEBHOOK' \
--header 'x-fic-verification-challenge: 292ff90a85ae68be5be1b2808a56cd183c3e8f72373b6cdda8e9dfd8e08f0f05'
GET /notifications HTTP/1.1
Host: example.com
User-Agent: FattureInCloud/API-WEBHOOK
x-fic-verification-challenge: 292ff90a85ae68be5be1b2808a56cd183c3e8f72373b6cdda8e9dfd8e08f0f05
To be able to validate the request, the Target must be able to get the x-fic-verification-challenge header's value and insert it in the JSON response body as follows:
{
"verification": "292ff90a85ae68be5be1b2808a56cd183c3e8f72373b6cdda8e9dfd8e08f0f05"
}
If Fatture in Cloud receives a 200 OK answer with this value included in the body, the subscription will be considered valid and Fatture in Cloud will start sending the required events to the Target.
🎉 Welcome!
Once the verification step is complete, the messages will start being sent to your application.
Since the first message could arrive in a few seconds or even days after the verification (it depends on when the first event is performed on the resources owned by the company), we decided to send a special Welcome Event to your endpoint when the verification step is concluded.
The Welcome Event will be similar to the other Events, but it will be recognizable by its special type it.fattureincloud.webhooks.subscriptions.welcome
.
Since it is just meant to give you a confirmation about the verification step, you can just ignore and discard this message or you could use it for your purposes: it's up to you.
The Welcome event will be sent as one of the first messages for that subscription, but we cannot assure you that it will be the first message in every case.
For example, if an event was generated during the validation procedure, it may be sent to the target before the Welcome Event, so we suggest using it just to confirm that the subscription was established correctly.
Check the Notifications page to find an example of a Welcome event.
🧮 List the subscriptions
If you want to check your active subscriptions on a certain company, you can use the List Subscriptions method.
Below you can find an example:
- cURL
- HTTP
curl --location --request GET 'https://api-v2.fattureincloud.it/c/company_id/subscriptions' \
--header 'Authorization: Bearer ACCESS_TOKEN'
GET /c/company_id/subscriptions HTTP/1.1
Host: api-v2.fattureincloud.it
Authorization: Bearer ACCESS_TOKEN
The Response Body will be similar to this one:
{
"data": [
{
"id": "SUB155",
"sink": "https://example.com/notifications",
"verified": true,
"types": ["it.fattureincloud.webhooks.entities.clients.delete"],
"config": {
"mapping": "binary"
}
},
{
"id": "SUB230",
"sink": "https://example.com/webhooks",
"verified": true,
"types": [
"it.fattureincloud.webhooks.entities.suppliers.delete",
"it.fattureincloud.webhooks.issued_documents.invoices.update",
"it.fattureincloud.webhooks.issued_documents.quotes.update",
"it.fattureincloud.webhooks.issued_documents.proformas.update",
"it.fattureincloud.webhooks.issued_documents.receipts.update",
"it.fattureincloud.webhooks.issued_documents.delivery_notes.update",
"it.fattureincloud.webhooks.issued_documents.credit_notes.update",
"it.fattureincloud.webhooks.issued_documents.orders.update",
"it.fattureincloud.webhooks.issued_documents.work_reports.update",
"it.fattureincloud.webhooks.issued_documents.supplier_orders.update",
"it.fattureincloud.webhooks.issued_documents.self_invoices.update",
"it.fattureincloud.webhooks.received_documents.create"
],
"config": {
"mapping": "binary"
}
}
]
}
🍒 Get a subscription
If you know the Subscription ID, you can use the Get Subscription method to retrieve its current status.
Below you can find an example:
- cURL
- HTTP
curl --location --request GET 'https://api-v2.fattureincloud.it/c/company_id/subscriptions/SUB155' \
--header 'Authorization: Bearer ACCESS_TOKEN'
GET /c/company_id/subscriptions/SUB155 HTTP/1.1
Host: api-v2.fattureincloud.it
Authorization: Bearer ACCESS_TOKEN
And the Response Body will be similar to this:
{
"data": {
"id": "SUB155",
"sink": "https://www.test.com",
"verified": true,
"types": ["it.fattureincloud.webhooks.entities.clients.delete"],
"config": {
"mapping": "binary"
}
}
}
📝 Modify the subscription
If you want, you can modify an existing Subscription by using the Modify Subscription method.
Below you can find an example:
- cURL
- HTTP
curl --location --request PUT 'https://api-v2.fattureincloud.it/c/company_id/subscriptions/SUB155' \
--header 'Authorization: Bearer ACCESS_TOKEN' \
--header 'Content-Type: application/json' \
--data-raw '{
"data": {
"types": [
"it.fattureincloud.webhooks.entities.suppliers.delete"
]
}
}
'
PUT /c/company_id/subscriptions/SUB155 HTTP/1.1
Host: api-v2.fattureincloud.it
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json
Content-Length: 190
{
"data": {
"types": [
"it.fattureincloud.webhooks.entities.suppliers.delete"
]
}
}
The Response Body will look something like this:
{
"data": {
"id": "SUB255",
"sink": "https://www.test.com",
"verified": true,
"types": ["it.fattureincloud.webhooks.entities.suppliers.delete"],
"config": {
"mapping": "binary"
}
}
}
The main differences between this request and the Create request are the following:
- The HTTP verb in this case is PUT instead of POST
- You need to specify the Subscription ID in the URL since the subscription already exists
In most of the cases, after modifying a subscription you don't need to perform the Verification step a second time, because the subscription was already verified. An exception is the modification of the sink parameter: in this case, the subscription is invalidated, and you will receive a new Verification Event to the new Target endpoint.
🗑 Delete the subscription
If you don't need the subscription anymore, there are two main methods to remove a Subscription:
- Delete it explicitly
- Make your target return a 410 Gone error to an Event Notification
In both cases, some notifications could still be sent after the deletion of the subscription, but they should stop in a few minutes.
There is another method to stop receiving notifications, but it is a safeguard mechanism that we use to avoid sending messages to dismissed or faulty systems. Check this page for further info about Expirations.
💣 Explicit deletion
In this case, the user needs to perform a Delete Subscription call.
The request is something similar to:
- cURL
- HTTP
curl --location --request DELETE 'https://api-v2.fattureincloud.it/c/company_id/subscriptions/SUB155' \
--header 'Authorization: Bearer ACCESS_TOKEN'
DELETE /c/company_id/subscriptions/SUB155 HTTP/1.1
Host: api-v2.fattureincloud.it
Authorization: Bearer ACCESS_TOKEN
🚽 Gone Response
In this case, the Target will return a 410 Gone status to the next Notification Event sent by the webhooks. When the Webhooks service receives a 410 Gone on a notification request, it considers the target as dismissed and it immediately deletes the related subscription. Of course, in this case the subscription could stay active for a certain amount of time, until when the first event will be sent to the target.