Skip to content

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.

More resources