Use incoming webhooks to get real-time updates
Listen for events on your account so your integration can automatically trigger reactions.
Why to use webhooks
When building Amica integrations, your applications could receive events as they occur in your Amica accounts so that your backend systems can execute actions accordingly. To enable webhook events, you need to register webhook endpoints. Once they are appropriately set up, Amica can push real-time event data to your application’s webhook endpoint when events happen in your Amica account. Amica uses HTTPS to send webhook events to your app as a JSON payload with an Evento object. Receiving webhook events is particularly useful for listening to asynchronous events, such as when a company document is created or issued, a customer or maybe a payment is made or updated. WebHooks notify you when one or more events are triggered on the API.
Event overview
Amica generates event data that we can send you to inform you of activity in
your account. When an event occurs, Amica generates a new Event object. By
registering webhook endpoints, you enable Amica to automatically send Event
objects as part of POST requests to the registered webhook endpoint hosted by
your application. After your webhook endpoint receives the Event, your app can
run backend actions (for example, calling your shipping provider’s APIs to
schedule a shipment after you receive a Document.Emit
event).
Note
Make sure you return a 2xx code to confirm the webhook has been successfully processed by your integration. If a different response is returned, or we receive no answer, delivery will be attempted again, up to five times.
Event types
The following event types are currently supported:
Event type | Description |
---|---|
Create |
An object has been created. Applies to most entities (objects) handled by the Customer API. |
Update |
An object has been updated. Applies to most entities (objects) handled by the Customer API. |
Delete |
An object has been deleted. Applies to most entities (objects) handled by the Customer API. |
Emit |
Fired when a document is issued ("emitted"). Applies to Documento objects. |
Export |
Fired when a new XML is created (or exported) from a document. Currently applies to FatturaElettronicaXml objects. |
When an event is fired, its Type
property will be set at the appropriate value
as {Object}.{EventType}
. For example, when a new document is added to the API,
the Type
property of the corresponding event will be Documento.Create
.
The event returned by the webhook will contain the whole object involved by the
event. In a document creation event, the Event.Object
property will contain
the newly created document.
For more details on the Event object, the events log, and the events endpoint, see Events.
The WebHook object
The webhook object exposes the following properties:
Webhook property name | Description |
---|---|
Id |
Unique webhook ID. |
AziendaId |
Company for which the webhook should be active. Set to null if the webhook should be active for all companies. |
Url |
Endpoint in your integration that will receive the POST request. Example: https://myapp/myendpoint . Only https accepted. |
Enabled |
Wether the webhook is enabled or not. On webhook creation, this is set to true . |
Secret |
The secret used to generate webhook signatures. Returned on webhook creation and never updatable. |
Description |
Optional description of the webhook is used or meant for. |
Events |
List of event types that will trigger the webhook. Example: ["Documento.Create"] |
ConcurrencyToken |
Concurrency tokens are used to implement optimistic concurrency control. |
Register and manage your webhooks
You create a webhook by sending a POST
request to the
api/v{version}/config/webhook/
endpoint. The response contains complete
WebHook object which includes its secret, that you later use to verify incoming
requests to your integration.
The request below:
curl --location 'https://{baseAddress}/api/v{version}/config/webhook' \
--header 'X-Amica20-ApplicationId: curl' \
--header 'X-Amica20-ApiKey: {apiKey}' \
--header 'X-Amica20-LicenseKey: {licenseKey}' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {token}' \
--data '{
"AziendaId": 1,
"Url": "https://myapp/myendpoint",
"Description": "my test endpoint",
"Events":["Documento.Create"]
}'
Would receive a response like this one:
{
"aziendaId":1,
"url":"https://ciao/ciao",
"enabled":true,
"secret":"whsec_3GRoXFqMCwZZe5Dbwoueg6wwQ4132FYB",
"description":"test",
"events":["Documento.Create"],
"id":6,
"concurrencyToken":844919
}
Notice that the request does not include secret
and enabled
values. These
are set by the API, as you can see in the response payload.
Make sure you safely store your endpoint secret
Currently, there's no other way to know your endpoint secret but at webhook
creation. For improved safety, GET
and LIST
requests will return a
null
secret. Also, you won't be able to change or require a new secret,
so make sure your newly returned endpoint secret is safely stored away. We
plan to add ways to re-roll endpoint secrets in the future, as well as the
option to retrieve them via a web UI.
For more information on how to leverage secrets to verify request signatures, see WebHook secrets below.
- You update a webhook with a
PUT
request to the webhook endpoint (api/v{version}/config/webhook/{id}
), although update requests won't change the original webhook secret. - You delete a webhook with a
DELETE
request to the webhook endpoint.
Best practices for using webhooks
Review these best practices to make sure your webhooks remain secure and function well with your integration.
Handle duplicate events
Webhook endpoints might occasionally receive the same event more than once. You
can guard against duplicated event receipts by making your event processing
idempotent. One way of doing this
is logging the events you’ve processed, and then not processing already-logged
events. You can use the EventGuid
event property for that.
Only listen to event types your integration requires
Configure your webhook endpoints to receive only the types of events required by your integration. Listening for extra events (or all events) puts undue strain on your server and we don’t recommend it. You can change the events that a webhook endpoint receives in the the API.
Quickly return a 2xx response
Your endpoint must quickly return a successful status code (2xx) prior to any complex logic that could cause a timeout.
Exempt webhook route from CSRF protection
If you’re using Rails, Django, or another web framework, your site might automatically check that every POST request contains a CSRF token. This is an important security feature that helps protect you and your users from cross-site request forgery attempts. However, this security measure might also prevent your site from processing legitimate events. If so, you might need to exempt the webhooks route from CSRF protection.
class AmicaController < ApplicationController
# If your controller accepts requests other than Amica webhooks,
# you'll probably want to use `protect_from_forgery` to add CSRF
# protection for your application. But don't forget to exempt
# your webhook route!
protect_from_forgery except: :webhook
def webhook
# Process webhook data in `params`
end
end
import json
# Webhooks are always sent as HTTP POST requests, so ensure
# that only POST requests reach your webhook view by
# decorating `webhook()` with `require_POST`.
#
# To ensure that the webhook view can receive webhooks,
# also decorate `webhook()` with `csrf_exempt`.
@require_POST
@csrf_exempt
def webhook(request):
# Process webhook data in `request.body`
Verify events are sent from Amica
Amica sends webhook events from a set list of IP addresses. Only trust events coming from these IP addresses.
Amica IP Addresses |
---|
51.254.6.80 |
Additionally, verify webhook signatures to confirm that received events are sent
from Amica. Amica signs webhook events it sends to your endpoints by including a
signature in each event’s Amica-Signature
header. This allows you to verify
that the events were sent by Amica, not by a third party. You can verify
signatures either using our official libraries, or verify manually using your
own solution.
WebHook signatures
Amica generates a unique secret for each endpoint. If you use multiple endpoints, you must obtain a secret for each one you want to verify signatures on.
Verifying signatures using our official libraries
You should always verify the signatures of incoming webhook payloads.
Use one of our official libraries to verify signatures; we recommend this
approach to verify signatures. You perform the verification by providing the
event payload, the Amica-Signature
header, and the endpoint’s secret. If
verification fails, you get an error.
In this .NET SDK example, a WebAPI controller leverages the utility
method ConstructEvent
to verify the signature:
[ApiController]
[Route("[controller]")]
public class DocumentoController : ControllerBase
{
[HttpPost]
public async Task<ActionResult> Post()
{
var json = await new StreamReader(Request.Body).ReadToEndAsync();
var signature = Request.Headers["Amica-Signature"].ToString();
var secret = Environment.GetEnvironmentVariable("WEBHOOK_SECRET");
var @event = ConstructEvent(json, signature, secret!);
var documento = (Documento)@event!.Object;
Console.WriteLine($"Event '{@event!.EventGuid}' recently fired of type '{@event.Type}' for company ID '{@event.CompanyId}'. Application was '{@event.ApplicationId}'.");
Console.WriteLine($"Document number: {documento.Numero}");
return Ok();
}
}
Please take note that the webhook secret is retrieved via an environment variable, it is not hard-coded in the source.
Preventing replay attacks
A replay attack is when an attacker intercepts a valid payload and its
signature, then re-transmits them. To mitigate such attacks, Amica includes a
timestamp in the Amica-Signature
header. Because this timestamp is part of the
signed payload, it’s also verified by the signature, so an attacker can’t change
the timestamp without invalidating the signature. If the signature is valid but
the timestamp is too old, you can have your application reject the payload.
Our libraries have a default tolerance of 5 minutes between the timestamp and
the current time. In the example C# snippet above, ContructEvent
would fail if
more than 5 minutes have passed since the signature's timestamp. You can change
this tolerance by providing an additional parameter when verifying signatures.
Use Network Time Protocol (NTP) to make sure that your server’s clock is
accurate and synchronises with the time on Amica’s servers.
Manually verifying webhook signatures
Although we recommend that you use our official libraries to verify webhook event signatures, you can create a custom solution by following this section.
The Amica-Signature
header included in each signed event contains a timestamp
and one or more signatures that you must verify. The timestamp is prefixed by
t=
, and each signature is prefixed by a scheme. Schemes start with v
, followed
by an integer. Currently, the only valid live signature scheme is v2
.
Amica-Signature:
t=1492774577,
v2=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
Note
We’ve added newlines for clarity, but a real Amica-Signature
header is on a single line.
Amica generates signatures using a hash-based message authentication code (HMAC) with SHA-256. To prevent downgrade attacks, ignore all schemes that are not v2.
To create a manual solution for verifying signatures, you must complete the following steps:
Step 1: Extract the timestamp and signatures from the header
Split the header using the ,
character as the separator to get a list of
elements. Then split each element using the =
character as the separator to
get a prefix and value pair. The value for the prefix t
corresponds to the
timestamp, and v2
corresponds to the signature. You can discard all other
elements.
Step 2: Prepare the signed_payload
string
The signed_payload
string is created by concatenating:
- The timestamp (as a string)
- The character
.
- The actual JSON payload (that is, the request body)
Step 3: Determine the expected signature
Compute an HMAC with the SHA256 hash function. Use the endpoint’s signing secret
as the key, and use the signed_payload
string as the message.
Step 4: Compare the signatures
Compare the signature in the header to the expected signature. For an equality match, compute the difference between the current timestamp and the received timestamp, then decide if the difference is within your tolerance.
To protect against timing attacks, use a constant-time-string comparison to compare the expected signature to each of the received signatures.
Limitations
The legacy Amica 20 for Windows application does not use the cloud APIs
documented on this website. Currently, only Documento
write events and the
Documento.Emit
event are logged by that application, so keep that in mind when
planning your integration. The Windows application won't trigger existing
WebHooks bound to other events. The official web application supports
the full range of events and WebHooks.