Webhooks
The FastSign API has support for webhooks. With webhooks, the FastSign platform
tells your application about interesting events, instead of your application
issuing API requests on an interval, checking for updated data.
The part of your application "listening" for events is called a webhook consumer.
FastSign is a webhook provider.
Supported events
At this time, the following events are issued by the FastSign platform:
contract.signed - Happens when a contract is accepted, signed, by all parties.
contract.rejected - Happens when a contract is rejected by a party.
The events contract.signed and contract.rejected are mutually exclusive.
That means only one of each event can ever be emitted for each contract. For
example, if contract.rejected once has been emitted, a contract.signed will
never happen for the same contract.
There is no possibility to only subscribe for a selected range of events. All
types of events till be sent to your webhook consumer.
How to set up your consumer
FastSign provides two different mechanism for registering webhook consumers;
via the API; and by logging in to the FastSign platform as customer admin.
Set up a consumer via the API
Prerequisite: Having a customer API token. A partner API token can't create
consumers.
Send an API request to /api/webhooks according to the
API documentation.
The url field is required.
The response to your request will contain two important fields:
public_key and key_id. Store these, as they are required when validating
each webhook event sent to your consumer!
Set up a consumer via the portal
- Log in to the FastSign portal as a customer admin.
- Navigate to the API page in top menu.
- Click the Webhooks link.
- Click the Ny webhook button.
- Fill in the required URL field and submit the form.
- You will be presented the page Redigera webhook. Please store the values
presented in the fields Publik nyckel and Key-ID. They are required
when validating each webhook event sent to your consumer!
The webhook journey
- An event occurs inside the FastSign platform. FastSign creates webhook events
with a payload containing the event type and a
unique identifier of the originating resource.
See payload examples.
- The webhook event is put in a queue waiting to be sent, which normally
happens after a couple of seconds.
- FastSign creates a signed HTTP request using the
IETF RFC 9421 standard. The
request is signed with a private key which together with the public key you
got when registering the consumer creates a key pair.
- The signed HTTP request is sent to the consumer URL. FastSign expects to get
a response back within 10 seconds.
- The consumer handles the HTTP request by validating the signature. If the
signature validation fails, stop further processing and return HTTP status
code 400. If the consumer successfully handled the
webhook event the response should have a 200 HTTP status code. If the
consumer is occupied with other tasks, a 429 status code can be returned
instead and FastSign will retry the event at later time.
- If the consumer could validate the signature of the HTTP request, it can
continue reacting upon the event. One probable first step is to send an API
request to FastSign to the URL in the
data.id event payload key.
Payload examples
Code samples
{
"type": "contract.signed",
"timestamp": "2026-04-05 13:37:42",
"data": {
"id": "/api/contracts/366478"
}
}
A webhook payload is the entire body of an HTTP request sent from FastSign to a
webhook consumer. The payload is always in JSON format with the three root keys
type, timestamp and data.
The value of type is one of the strings seen in the list under previous
headline "Supported events".
The value of timestamp is a string with the date and time when the event
occurred in the FastSign platform in the Europe/Stockholm timezone. The format
of the timestamp is an ISO 8601 date, followed by a space and then an ISO 8601
time with hours, minutes and seconds.
The value of data is an object with an id key. The id value is meant to be
used to create an API request to get the full resource data the event was about.
Security features
Code samples
POST /your-consumer-url HTTP/1.1
Host: your-application.example.com
Date: Thu, 02 Apr 2026 21:03:19 GMT
Content-Digest: sha-256=:h+BmbUkuPKwVB7h+S/Q1MTd62s0D6AYRGc8GvNEHeiw=:
Signature-Input: sig1=("@method" "@path" "host" "date" "content-digest");keyid="ed25519-1";alg="ed25519";created=1775163799;expires=1775163829
Signature: sig1=:ZCJfhBFYCzLQ9wWgtKo9+XtKpji2KSYhdxgY33XWaGKit5aBKgpN6k9JXRt9faK/I9Xh4WGWR6ffxbifnHObCA==:
Content-Type: application/json
{"type":"contract.rejected","timestamp":"2026-04-02 22:57:56","data":{"id":"\/api\/contracts\/68"}}
The code sample shows how an HTTP request looks like as seen by a webhook
consumer. It contains three security features which must be validated together:
- The
Content-Digest HTTP header which is a hash of the payload, the body.
If the consumer calculates the same hash, that is a proof that the payload was
unchanged during transport.
- The
expires part of the Signature-Input HTTP header. It is a UNIX timestamp
when the HTTP message is expired. This helps prevent replay attacks.
- The
Signature HTTP header, a cryptographically calculated signature
according to the
IETF RFC 9421 standard.
The signature can only be created by the private key kept secret inside FastSign
and be verified using the public key. If the consumer can validate the signature,
this is a proof that FastSign actually send the HTTP request.
The signature contains both the expires and Content-Digest.
We strongly recommend that you use a library for your programming language for
validating the signature. A curated list of such libraries can be found at
httpsig.org website. For the PHP language, we
recommend the Composer package
macgirvin/http-message-signer.
You can also do a GitHub search for RFC 9421
and filter by your programming language.
Make sure that your selected library has support for the ED25519 algorithm.
Example of validating an event
Code samples
<?php declare(strict_types=1);
use HttpSignature\HttpMessageSigner;
use Nyholm\Psr7Server\ServerRequestCreator;
use Nyholm\Psr7\Factory\Psr17Factory;
$psr17_factory = new Psr17Factory();
$creator = new ServerRequestCreator(
$psr17_factory,
$psr17_factory,
$psr17_factory,
$psr17_factory,
);
$request = $creator->fromGlobals();
$public_key = "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAltyTaOMgdu4SgBvY691THCFlCqgyWfXzm/GhF4fc39w=\n-----END PUBLIC KEY-----";
$key_id = 'ed25519-1';
$verifier = (new HttpMessageSigner())
->setPublicKey($public_key)
->setKeyId($key_id)
;
$is_valid = $verifier->verifyRequest($request);
if (!$is_valid) {
http_response_code(400);
exit();
}
$body = (string) $request->getBody();
$event = json_decode($body, true, 2, JSON_THROW_ON_ERROR);
This is a sample implementation of a webhook consumer in PHP using the recommended
Composer package
macgirvin/http-message-signer.
Step 1: Create a PSR-7 request object
As this library uses the PSR-7 standard for
HTTP messages, the sample begins with constructing a request object from the PHP
super globals ($_SERVER, $_POST etc.) using the Composer package
nyholm/psr7-server.
It's possible that your framework of choice already has a PSR-7 implementation,
consult the documentation of your framework and search for PSR-7.
Step 2: Configuration
The message signer library needs two configuration values; the public key and
the key ID. Both of these values are created when the consumer was
registered in FastSign.
Step 3: Validation of signature
A signer object is created from the library using the configuration values from
step 3.
Using the signer object, called $verifier here, we call it's verifyRequest()
method using the $request from step 1 as only argument. The return value is
stored in the $is_valid variable as a boolean.
Step 4: Handle validation result
The $is_valid variable from step 3 must be checked to see if the signature was
valid or not. A value of true means the signature was valid.
In this example, we check if $is_valid is false and in that case stops the
processing of event and sets the HTTP response code to 400.
Retries (temporary errors)
In some circumstances the sending of a webhook event can be retried by the
FastSign platform:
- If the consumer returned an HTTP status code in ranges 500 to 599,
- or an HTTP status code in ranges 400 to 499 except code 410.
An event will be retried following a time schedule:
| Delay between each send |
Time since event occured |
| 5 minutes |
5 minutes |
| 30 minutes |
35 minutes |
| 2 hours |
2 hours, 35 minutes |
| 5 hours |
7 hours, 35 minutes |
| 10 hours |
17 hours, 35 minutes |
| 14 hours |
31 hours, 35 minutes |
| 20 hours |
51 hours, 35 minutes |
| Full stop |
75 hours, 35 minutes |
An event can also be retried manually from inside the event log, available from
the same page as registering a consumer. All
events are saved in the log for one month.
Permanent errors
If a consumer misbehaves, FastSign will disable it after 10 permanent errors.
The following counts as a permanent error:
- If the consumer returns no response after 10 seconds of waiting,
- or if the consumer returns an HTTP status code 410,
- or an HTTP status code in ranges 300 to 399.
A disabled consumer will not receive more webhook events until a customer admin
logs in to the FastSign portal and enables the consumer again.
When a consumer is disabled, an e-mail will be sent to either the e-mail used
to register the customer or to the special field contact_email available when
registering a consumer. An example of usage for contact_email is when a
technical contact needs to be notified about the webhook consumer instead of
the customer admin.