# Hook v1

Use hook to be notified about events in the Procurement Service Bus. The Procurement Service Bus can send webhooks and email notifications to your application about events that happened in your Party. This mechanism is very useful for receiving invoices. Once an invoice has arrived you will be notified via the registered hooks.

There are several types of hooks. The type of hook depends on the action defined. The two general actions are 'mail' and 'https'.

Hooks can be triggered at several moments. These moments are defined as topics.

# Environment hooks

Receive hook notifications for all the parties within your environment. This is very useful when all party hooks have the same configuration. Using the environment hook will make the partyId hook unnecessary in most cases, and thus reduce the calls that have to be made to enroll a party in the PSB. By default environment hooks run with one queue. This means that the PSB can only send one request at a time. If you need higher throughput, please let us know and we will scale your hook to use multiple parallel queues instead.

DO use the environment hook when certain events for all parties needs to be delivered to one endpoint url.

DO NOT use the environment hook if the endpoint url is different for each partyId.

# Topics

The topic indicates when the hook is triggered.

Topic Function
InvoiceReceived Triggered when a document is successfully received by the Procurement Service Bus.
InvoiceReceivedError Triggered on a document that is rejected by the Procurement Service Bus because of an error.
InvoiceSent Triggered when a document is successfully sent by the Procurement Service Bus and received by another party.
InvoiceSentError Triggered on a document that is rejected by the receiver because of an error. This is a permanent error and the document will not be send.
InvoiceSentRetry Triggered on a document that is rejected by the receiver because of an error. This can be a temporary failure. Retries will be done.
SpecialHooks See if hooks are working correctly. Only the email action can be used.
HookSent Triggers when a hook is triggered and a hook message is sent. This means the receiving system is informed about a topic.
HookSentRetry Triggers when a hook is triggered but the message was not send because the receiver can’t be reached and a retry will be done. This is an indication that your hook receiver is not working correctly and the Procurement Service Bus is unable to inform you about the status of processed documents.
HookSentError Triggers when a hook has stopped retrying. This is a permanent error.

# Filters

Filters can be used to add certain conditions to trigger hooks. For example, only trigger a ReviewReceived hook when the verdict is "reject" or only trigger an "InvoiceReceived" hook when the sender is a given party.

# Adding a filter

Filters can be added via the POST /api/v1/{partyId}/hook endpoint, using the "filter" field.

{ 
  "id": "1", 
  "action": "mailto:techsupport@econnect.eu", 
  "name": "filter hook ", 
  "topics": [ 
    "ReviewReceived"
  ], 
  "filter": "verdict == \"reject\"",
  "isActive": true 
} 

This hook will only trigger when the verdict for the received review is "reject".

Filters use a lambda syntax and allow multiple conditions in one filter, for example:

"sender == \"0106:123\" || sender == \"0106:456\" && verdict.StartsWith(\"acc\")"

# Mail hooks

A mail hook has the following action: "action":"mailto:receiver@example.com".

# Include attachment

Mail hooks can also include the document as an attachment, for monitoring or debug purposes for example. To do this, add includeAttachment=true as a query parameter to the action. Optionally, query parameter targetDocumentTypeId={target} can be used to specify a target documentTypeId. The attached document will be of this documentTypeId, given a transformation from the original to the target documentTypeId is available. Special characters in the documentTypeId have to be escaped.

"action":"mailto:receiver@example.com?includeAttachment=true&targetDocumentTypeId=urn%3Aoasis%3Anames%3Aspecification%3Aubl%3Aschema%3Axsd%3AInvoice-2%3A%3AInvoice%23%23urn%3Acen.eu%3Aen16931%3A2017%23compliant%23urn%3Afdc%3Anen.nl%3Anlcius%3Av1.0%3A%3A2.1"

# WebHooks

Webhooks deliver information by pushing a https message to a receiver. This can only be done with https.

# Configure your webhook

You can register the webhook via PUT method on the /Hook endpoint. The Procurement Service Bus starts sending HTTP POST webhooks once the webhook is registered. Events will only be send when a hook is registered with the corresponding topic. Define an action with your endpoint URL and append this URL with a # and a secret key. This key will be used to sign requests. Optionally, you can define basic authentication in the action to protect your endpoint in the form of: https://user:pass@your.endpoint#someRandomKey.

{
   "id": "1",
   "name": "webhook",
   "action": "https://user:pass@webhook.site/a81a0be5-8aad-4110-a40c-aa41a2c6e9ac#secretKey",
   "topics": [
     "InvoiceReceived"
   ],
   "isActive": true
}

# Testing webhook

For testing the call GET /api/v1/{partyId}/hook/ping can be used. This call will trigger the InvoiceReceived hook by sending a dummy document to {partyId}.

# Securing webhook

We use an HMAC SHA256 signature of the payload and include that signature in the request header 'X-EConnect-Signature'. The header value is in the form of 'sha256={signature}', where {signature} is a 64-byte, hexadecimal representation of a SHA256 signature. The signature is computed using a secret key provided by you. Validate this signature to make sure the data is not manipulated and sent from the PSB.

The payload contains a 'sentOn' field. Make sure that this date is not older than 5 minutes to prevent relay attacks.

You could also whitelist our IP addresses.

# Validate Webhook signatures

  1. Get the signature from the X-EConnect-Signature header.
  2. Get the json payload from the request body and your secret key.
  3. Compute the signature using the payload and your secret key. Then stringify the byte[] to a hexadecimal representation and prepend the string with "sha256=".
  4. Compare the signatures. (We advise to use a secure compare.)

You could use https://www.freeformatter.com/hmac-generator.html (opens new window) to manually validate the signature. Enter your secret key and select SHA256 as the digest algorithm, then compute the hash.

Compute signature example:

public static string ComputeSignature(string message, string secret)
{
	var encoding = new UTF8Encoding();
	byte[] keyByte = encoding.GetBytes(secret);
	byte[] messageBytes = encoding.GetBytes(message);
	using var hmacSha256 = new HMACSHA256(keyByte);
	byte[] messageHash = hmacSha256.ComputeHash(messageBytes);

	var builder = new StringBuilder();
	builder.Append("sha256=");
	foreach (var b in messageHash)
	{
		builder.Append(b.ToString("x2"));
	}

	return builder.ToString();
}

Or take a look at the full webhook receival example build in PHP (opens new window).

# Webhook using mutual SSL

With the endpoint PUT /api/v1/{partyId}/hook in the PSB Api you can setup a hook with mutual SSL authentication. To be able to use mutual SSL the “Init” object can be used. In this object the certificate can be added as base64 encoded string:

{
  "id": "1",
  "name": "ssl hook",
  "action": "https://econnect.eu#secretKey",
  "init": {"certificate": "MIIKngIBAzCCCloGCSqGSIb3DQEHAaCCCksEggpHMIIKQzCCBgQGCSqGSIb3DQEHAaCCBfUEggXxMIIF7TCCBekGC(…)"},
  "topics": [
    "InvoiceReceived",
    "InvoiceReceivedError",
    "InvoiceSent",	
    "InvoiceSentError",
    "InvoiceSentRetry"
  ],
  "publishTopics": null,
  "isActive": true
}

Setting a certificate is one-time action. The certificate is then saved when the hook is modified. When you update the hook, for example by adding a topic, you do not have to provide the certificate again. The certificate can no longer be read back via the GET / hook. Currently PEM and PKCS # 12 (.pfx and .p12) certificates are supported. The certificate must contain the public and private key and must not be password protected. To create a base64 encoded string from a certificate you can use OpenSSL, or the following Powershell command:

$fileContentBytes = get-content 'C:certificaat.pfx' -Encoding Byte
[System.Convert]::ToBase64String($fileContentBytes) | Out-File ‘certificaat_base64string.txt’

# Responding to a webhook

To acknowledge receipt of a webhook your endpoint should return a 2xx HTTP status code. Any response code outside of this range, including 3xx codes, will indicate that you did not receive the webhook. The default webhook timeout is 100 seconds, we expect any response code from your side within 100 seconds.

We will attempt to deliver your webhooks for up to five days, with an exponential time increase between retries. Set up a mail hook with the topic HookSentError to be notified about delivery failures. The webhooks cannot be triggered manually.

We only accept https endpoints with a valid server certificate.

Disable any CSRF protection on your webhook endpoints to avoid the webhook being blocked.

# Returned elements

Element Description
topic Topic that triggered the hook.
partyId The hook's owner. The party that sent or received a document.
hookId The hook that triggered the message.
documentId Id of the document that was processed.
message Description why the hook was triggered.
details A dictionary containing more information specific to the topic.
createdOn Original date the hook was triggered.
sentOn Date the hook was actually sent.

The details element is dynamic, it is possible this will contain new fields in the future.

# Https Basic

Push invoices to our HTTPS endpoint (HTTPS with basic features).

# Configure the https endpoint (inbound or outbound)

Configuring the endpoint works much the same way as a webhook; Register using the PUT method on the hook endpoint.
The endpoint will be available using a generated token from the result of the PUT method.

This should only be used when the regular API is not an option (when tokens are required).

{
   "id": "1",
   "name": "https inbound",
   "action": "httpsin://user:pass@inbound?token=$token$",
   "publishTopics": [
     "ReceiveInvoice"
   ],
   "isActive": true
}

The $token$ part will be replaced with the generated token, which can then be used on our endpoint.
The endpoint will be /trigger/https/v1/$token$ where $token$ needs to be replaced with the generated token.

# Configure the https endpoint (outbound)

It's also possible to configure the https endpoint to send invoices instead of receiving them. Only the publishTopics should be changed to SendInvoice or equivalents, and the action should be set to outbound. This ensures the eConnect validation does not fail during the delivery process.

{
   "id": "1",
   "name": "https outbound",
   "action": "httpsin://user:pass@outbound?token=$token$",
   "publishTopics": [
     "SendInvoice"
   ],
   "isActive": true
}

Just like before, the $token$ part will be replaced with the generated token, which can then be used on our endpoint.
The endpoint will be /trigger/https/v1/$token$ where $token$ needs to be replaced with the generated token.

# Sending an invoice using https with basic authentication

The endpoint under the token will have been configured after the action has been set.
It is now possible to send invoices to the endpoint by POST with the invoice in the attachment body.
Validation is done asynchronously, hence a mail hook should be configured.
If user:pass has been set in the action, the basic authentication header will need to be present.

# Batch

Instead of handling every event one by one, it's also possible to create batches with a hook with the batch action.

The batch hook will collect all events with a certain topic for a given period and will then create a batch of all the documents connected to those events. This way it's possible to for example create a ZIP file with all the received invoices from the last hour.

# Configure the batch hook

The action for the batch hook looks like this:

batch://zip?period=00:15:00&maxBatchSize=100&excludePrimaryAttachment=false&includeAdditionalAttachments=true&targetDocumentTypeId={documentTypeId}

For now the only supported batch action is zip. This action will create a ZIP file containing all the collected files.

The period parameter determines how long the hook waits for new events after the first event of a batch is received. If the maxBatchSize is reached before the period is over the hook will stop collecting new events and start with the batch action, like creating a zip file. When the batch action is done, the hook will start collecting events for the configured period again. The default period is 15 minutes and the default maxBatchSize is 100.

The excludePrimaryAttachment and includeAdditionalAttachments can be used to influence the files that are added to zip file. By default the primary attachment (i.e. the XML invoice) will be added to the zip. includeAdditionalAttachments can be set to true to also add the AdditionalDocumentReference’s from the XML invoice, like a PDF attachment, to the zip as separate files.

Optionally, query parameter targetDocumentTypeId can be used to specify a target DocumentTypeId. The primary attachment will be of this documentTypeId, given a transformation from the original to the target DocumentTypeId is available. Special characters in the documentTypeId have to be escaped.

A batch hook supports only one topic at a time and publishes that topic as -Batched after the batch action is complete. For example, a hook that batches InvoiceReceived events publishes events with a InvoiceReceivedBatched(Error/Retry) topic.

A full hook example looks like this:

{
  "id": "1",
  "name": "batch hook",
  "action": "batch://zip?period=00:15:00&maxBatchSize=100&excludePrimaryAttachment=false&includeAdditionalAttachments=true&targetDocumentTypeId=urn%3Aoasis%3Anames%3Aspecification%3Aubl%3Aschema%3Axsd%3AInvoice-2%3A%3AInvoice%23%23urn%3Acen.eu%3Aen16931%3A2017%23compliant%23urn%3Afdc%3Anen.nl%3Anlcius%3Av1.0%3A%3A2.1",
  "topics": [
    "InvoiceReceived"
  ],
  "isActive": true,
  "createdOn": "2023-04-03T14:07:24.8813007+02:00"
}
© 2024 eConnect International B.V.