Working with Webhooks

Use the Webhook integration to get realtime updates

  1. What is webhooks
  2. What is CloudEvents
  3. Ingesting Webhooks
  4. FAQ

1. What is Webhooks

A webhook in web development is a method of augmenting or altering the behavior of a web page or web application with custom callbacks. These callbacks may be maintained, modified, and managed by third-party users and developers who may not necessarily be affiliated with the originating website or application. - Wikipedia

Webhooks at Zefort

In Zefort, webhooks can be used to get callbacks when certain events happen, such as new or updated contracts, documents, or event more specific contract field changes. The administrator of a Zefort account can choose which of these events they want to be sent callbacks for and does not need to ingest all types of events.

To not reinvent the wheel, Zefort uses the CloudEvents specifications for sending and wrapping webhooks. CloudEvents specifies both how the service sending and receiving webhooks should handle certain cases. This includes authentication, message structure, response and error codes as well as abuse protection. Zefort follows these specifications wherever possible and follows any new specification changes if they were to arrive.

2. What is CloudEvents

CloudEvents is a "specification for describing event data in a common way". Zefort specifically uses the Webhook specification, but CloudEvents does support much more than webhooks. Cloudevents takes care of the question "how should things look like" when it comes to messages. What it does not do is specify what data should be encapsulated inside a cloud event, just the metadata.

If there are any questions regarding how to handle messages, we strongly suggest having a look at the specification to learn more about how CloudEvent Webhooks are sent and handled.

3. Ingesting Webhooks

An examples of a json structured CloudEvent payload that Zefort sends:

{
    "specversion": "1.0",
    "id": "a143aeb7-3ec7-49d8-9ff1-a5ab09cb776e",
    "source": "zefort/webhook",
    "type": "document_created",
    "subject": "doc_1Jf6pQrSFevkeyHfT4",
    "time": "2022-11-07T14:04:48.519285+00:00",
    "data": {
        ...
    },
    "obj_type": "document",
    "verb": "created",
    "api_url": "https://.../api/documents/doc_1Jf6pQrSFevkeyHfT4/",
    "ui_url": "https://.../contracts/ct_1K3LlFiyPP/details/doc_1Jf6pQrSF"
}

Some of these fields must exist according to the CloudEvent, such as id, specversion, source. Some of the fields are optional fields, such as time and type. Lastly there are "extensions" that Zefort provides, such as the verb, obj_type, and URLs to objects that are not specified by Cloudevents at all, but are provided as extra fields.

Field explanation

Attribute Description
specversion CloudEvent specification version
id Unique UUID for an event
source Source service for the CloudEvent
type Event type (obj_type + verb)
subject In Zefort this is the id of the object represented in the data
time Original event timestamp
data Data of the object in the event
obj_type Object type that the event is about
verb Event that happened to the obj
api_url Zefort API endpoint for the object
ui_url Zefort UI URL for the object

Data structure

The data that is places in the data attribute varies depending on the obj_type. It follows the same structure as our API for the same type of object.

Type Structure
contract API GET /contract/<id>
document API GET /document/<id>
attribute Slightly extended version of /contract/<id> attributes attribute

Authorization

Zefort uses Authorization Request Header Field authorization for all requests. This means that Zefort sends an Authorization header with a bearer token with each request. The token can be used to verify that the requests originate from Zefort and not a malicious third-party. You can find the token in the integration settings.

Example request:

POST /webhooks HTTP/1.1
Host: yourserver.example.com
Authorization: Bearer 32940t8n3904t80re9gn8wenfg09s840ns490g

Request and response status codes

The service to which the webhook is sent must respond to each request. The only accepted status codes that are returned are 200 OK, 201 Created or 204 No Content. This in accordance with the specification.

The following happens if the service responds in a certain way:

Response What happens in Zefort
200 OK Event marked as delivered
201 Created Event marked as delivered
204 No Content Event marked as delivered
4XX-5XX (not 410/429) Webhook is retried
429 Too Many Requests Webhook is retried according to Retry-After header
410 Gone Integration is disabled
410 Gone Integration is disabled
3XX (Redirects) Integration is disabled
Timeout (5 sec) Integration is disabled

In the case of a 4xx-5xx response, Zefort will retry to send a message. It will retry for a minimum of 3 days. Zeforts retry logic follows an exponential curve with the maximum time between retries being 1 day. The first retry will be after 1 second, the second after 2s, then 4, 8, 16, 32, 64, 128, and so on. When all retries have been exhausted, the integration will be disabled.

In the case of a 429 response from the service, which is accompanied by a Retry-After header, Zefort does a best effort to retry according to the header.

Abuse protection

At the moment, Zefort does not support the abuse protection features specified in the CloudEvent Webhook specification. Zefort however manually vets all outgoing endpoints before they can be taken into use as a webhook destination. Adding a new endpoint does not work without first getting a green light from Zefort to use the endpoint.

4. FAQ

Are messages sent in-order?

Zefort does a best effort to send messages in order, it does however not guarantee it. All messages have a timestamp with the time of the event which should be checked if ordering is important. All messages that are retried also keep the same timestamp and ID as in the initial message, meaning that duplicates can be handled by checking if a message has already been handled.

Can messages be rate-limited from Zefort?

Zefort currently does not support pre-configured rate-limiting. This means that you can not set a specific rate-limit ahead of getting any messages. Zefort does support sending a 429 response with a Retry-After header which can specify how long to wait before retrying again.

We strongly recommend handling webhooks asynchronously as there might be cases where a lot of requests are made at the same time. This will happen for instance when bulk actions are done in Zefort, or a lot of contracts are added at the same time.

Why am I not receiving any requests?

This can be due to multiple reasons:

  • All webhook endpoints needs to be approved by Zefort before messages are sent. If you have changed your webhook endpoint but not got a green light from Zefort, then no messages will be sent.
  • Make sure that the integration is enabled.
  • Did you type in your webhook URL correctly?
  • Are you responding with a non-compliant response and the integration has been auto-disabled?

5. Examples

Contract created

{
    "specversion": "1.0",
    "id": "8e495872-c96e-4123-a0ad-230e7b2b8a9b",
    "source": "zefort/webhook",
    "type": "contract_created",
    "subject": "ct_1K3LlFiyPPNpKCJ8Qy",
    "time": "2022-11-07T14:04:48.508773+00:00",
    "data": {
        "attributes": {
            "title": null,
            "end_date": null,
            "effective_date": null,
            "signed_date": null,
            "main_language": null,
            "CUSTOM_vatid_001": null
        },
        "cover_document": null,
        "id": "ct_1K3LlFiyPPNpKCJ8Qy",
        "incomplete": false,
        "num_emails": 0,
        "num_files": 0,
        "num_users": 1,
        "owner": {
            "id": "user_1KfvWccrSnlx70g1XF",
            "email": "example@zefort.com",
            "name": "Thor Doe"
        },
        "parties": [],
        "permission": "full",
        "receive_time": "2022-11-07T14:04:48.477557Z",
        "related_contracts": [],
        "status": "queued",
        "is_duplicate": false,
        "favorite_of": [],
        "last_modified": "2022-11-07T14:04:48.479067Z",
        "trashed": false,
        "esigns": [],
        "num_signed_documents": 0,
        "activities": [],
        "allow_emails": false,
        "documents": [],
        "duplicates": [],
        "emails": [],
        "notes": [],
        "num_binders": 0,
        "rated": false,
        "taggings": []
    },
    "obj_type": "contract",
    "verb": "created",
    "api_url": "https://my.zefort.com/api/contracts/ct_1K3LlFiyPPNpKCJ8Qy/",
    "ui_url": "https://my.zefort.com/contracts/ct_1K3LlFiyPPNpKCJ8Qy"
}

Contract updated

{
    "specversion": "1.0",
    "id": "2120e9ce-d0a6-486a-85bf-76e5dea7bd17",
    "source": "zefort/webhook",
    "type": "contract_updated",
    "subject": "ct_1K3LlFiyPPNpKCJ8Qy",
    "time": "2022-11-07T14:04:48.741323+00:00",
    "data": {
        "attributes": {
            "title": null,
            "end_date": null,
            "effective_date": null,
            "signed_date": null,
            "main_language": null,
            "CUSTOM_vatid_001": null
        },
        "cover_document": {
            "id": "doc_1Jf6pQrSFevkeyHfT4",
            "content_type": "image/png",
            "role": "contract",
            "status": "queued",
            "num_pages": 0,
            "filename": "webhook_test.png",
            "receive_time": "2022-11-07T14:04:48.513376Z",
            "taggings": [],
            "ordinal": 1,
            "text_available": false,
            "valid_signature": null,
            "is_signable": null,
            "signatures": []
        },
        "id": "ct_1K3LlFiyPPNpKCJ8Qy",
        "incomplete": false,
        "num_emails": 0,
        "num_files": 1,
        "num_users": 1,
        "owner": {
            "id": "user_1KfvWccrSnlx70g1XF",
            "email": "example@zefort.com",
            "name": "Saga Doe"
        },
        "parties": [],
        "permission": "full",
        "receive_time": "2022-11-07T14:04:48.477557Z",
        "related_contracts": [],
        "status": "processing",
        "is_duplicate": false,
        "favorite_of": [],
        "last_modified": "2022-11-07T14:04:48.617280Z",
        "trashed": false,
        "esigns": [],
        "num_signed_documents": 0,
        "activities": [],
        "allow_emails": false,
        "documents": [
            {
                "id": "doc_1Jf6pQrSFevkeyHfT4",
                "content_type": "image/png",
                "role": "contract",
                "status": "queued",
                "num_pages": 0,
                "filename": "webhook_test.png",
                "receive_time": "2022-11-07T14:04:48.513376Z",
                "taggings": [],
                "ordinal": 1,
                "text_available": false,
                "valid_signature": null,
                "is_signable": null,
                "signatures": []
            }
        ],
        "duplicates": [],
        "emails": [],
        "notes": [],
        "num_binders": 0,
        "rated": false,
        "taggings": []
    },
    "obj_type": "contract",
    "verb": "updated",
    "api_url": "https://my.zefort.com/api/contracts/ct_1K3LlFiyPPNpKCJ8Qy/",
    "ui_url": "https://my.zefort.com/contracts/ct_1K3LlFiyPPNpKCJ8Qy"
}

Document created

{
    "specversion": "1.0",
    "id": "a143aeb7-3ec7-49d8-9ff1-a5ab09cb776e",
    "source": "zefort/webhook",
    "type": "document_created",
    "subject": "doc_1Jf6pQrSFevkeyHfT4",
    "time": "2022-11-07T14:04:48.519285+00:00",
    "data": {
        "id": "doc_1Jf6pQrSFevkeyHfT4",
        "content_type": "image/png",
        "role": "contract",
        "status": "queued",
        "num_pages": 0,
        "filename": "webhook_test.png",
        "receive_time": "2022-11-07T14:04:48.513376Z",
        "taggings": [],
        "ordinal": 1,
        "text_available": false,
        "valid_signature": null,
        "is_signable": null,
        "signatures": [],
        "contract": "ct_1K3LlFiyPPNpKCJ8Qy"
    },
    "obj_type": "document",
    "verb": "created",
    "api_url": "https://my.zefort.com/api/documents/doc_1Jf6pQrSFevkeyHfT4/",
    "ui_url": "https://my.zefort.com/contracts/ct_1K3LlFiyPPNpKCJ8Qy/details/doc_1Jf6pQrSFevkeyHfT4"
}

Attribute created

{
    "specversion": "1.0",
    "id": "6ffc083f-e9c3-4b11-8779-9b3874785a91",
    "source": "zefort/webhook",
    "type": "attribute_created",
    "time": "2022-11-08T07:00:34.361888+00:00",
    "data": {
        "contract": "ct_1L3XJMbGQcyEtlzF7R",
        "name": "title",
        "label": "Title",
        "value": "A new title",
        "timestamp": "2022-11-08T07:00:34.355977Z",
        "source": "",
        "from_default": null,
        "set_by": "user_1KfvWccrSnlx70g1XF"
    },
    "obj_type": "attribute",
    "verb": "created",
    "api_url": "https://my.zefort.com/api/contracts/ct_1L3XJMbGQcyEtlzF7R/",
    "ui_url": "https://my.zefort.com/contracts/ct_1L3XJMbGQcyEtlzF7R"
}