Webhooks

A webhook allows your application to receive real-time notifications when specific events occur. Webhooks can be configured through the PORTOS BackOffice application. For each webhook, you can define the following options:

  • URL: address where the events will be delivered

  • Secret: optional string that is used to create JSON payload signature.

  • Resource filter: specifies list of resources (e.g. tickets, article categories, ...) for which notification will be sent.

When an event is triggered, the PORTOS API sends an HTTP POST request to the specified URL, carrying the event data as a JSON payload. It is essential that the external application hosting the URL can handle these HTTP POST requests to process the event data effectively.

Testing Webhook Deliveries

  1. In your browser, navigate to https://smee.io

  2. Click Start a new channel. Smee.io will generate a unique Webhook Proxy URL for you.

  3. Copy the full URL under "Webhook Proxy URL".

  4. Open PORTOS BackOffice application.

  5. Navigate to System > Extensions > Webhooks (or Systรฉm > Rozลกรญrenia > Webhooky if using the Slovak version).

  6. Create a new webhook and paste the copied URL from Smee.io into the Address (Adresa) field.

  7. Press the Save (Uloลพiลฅ) button

  8. In PORTOS BackOffice, perform an action such as creating, editing, or deleting a resource (e.g., an article category).

  9. Check your Smee.io channel; the event should appear, confirming that the webhook is working correctly.

Validating Webhook Deliveries

To ensure the security and authenticity of webhook deliveries, users have the option to specify a "secret" when setting up a webhook. If a secret is defined, every webhook request from your app will include an HTTP header named X-PORTOS-WEBHOOK-SIGNATURE. This header contains an HMACSHA256 signature of the JSON payload, generated using the secret. External applications can use this signature to validate that the webhook requests are genuinely from your app and have not been tampered with.

Example Implementations

To implement HMAC verification in your app, please refer to examples below. Examples are using following variables:

  • jsonPayload: The content of the HTTP request body that contains the event data in JSON format.

  • receivedSignature : The value of the X-PORTOS-WEBHOOK-SIGNATURE HTTP header, which contains the HMACSHA256 signature sent by the PORTOS API.

  • secret: The secret key specified in webhook settings, used to generate the signature.

using System.Security.Cryptography;
using System.Text;

public bool ValidateWebhookSignature(string jsonPayload, string receivedSignature, string secret)
{
    // Convert the secret to a byte array
    var secretKey = Encoding.UTF8.GetBytes(secret);

    // Create HMACSHA256 using the secret key
    using (var hmac = new HMACSHA256(secretKey))
    {
        // Compute the hash of the JSON payload
        var payloadBytes = Encoding.UTF8.GetBytes(jsonPayload);
        var computedHash = hmac.ComputeHash(payloadBytes);

        // Convert the computed hash to a hex string
        var computedSignature = BitConverter.ToString(computedHash).Replace("-", "").ToLower();

        // Compare the computed signature with the received signature
        return computedSignature == receivedSignature.ToLower();
    }
}

Example Hash calculation

Hereโ€™s an example of how to calculate the SHA256 hash for a webhook using the provided secret and JSON content:

Secret: my-secret-key

JSON content:

{"id":"c32314f8-39c2-4d03-864e-201586637657","version":"1.0","event":"portos.resources.changed","data":{"user":{"name":"Majiteฤพ","userName":"999","featureName":null,"deviceName":"BackOffice"},"resourceName":"articleCategories","action":"Updated","resource":{"label":"PIV","description":"Pivรก","customerDescription":"","courseNumber":null,"color":"#FF9800","sortHint":null,"tags":[],"ordering":null}}}

Resulting SHA256 hash: 06324bd73f157373a4a678320a014d8c3db4c2e07d8a6177ecdf233b9f07d3f0

Webhook payload

Data models

The webhook content follows this data structure:

WebhookPayloadDto

WebhookResourceChangedContentDto

UserInfoIdentity

Webhook Payload Example

Please note that the actual payload does not include JSON indentation.

{
    "id": "c32314f8-39c2-4d03-864e-201586637657",
    "version": "1.0",
    "event": "portos.resources.changed",
    "data":
    {
        "user":
        {
            "name": "Majiteฤพ",
            "userName": "999",
            "featureName": null,
            "deviceName": "BackOffice"
        },
        "resourceName": "articleCategories",
        "action": "Updated",
        "resource":
        {
            "label": "PIV",
            "description": "Pivรก",
            "customerDescription": "",
            "courseNumber": null,
            "color": "#FF9800",
            "sortHint": null,
            "tags":
            [],
            "ordering": null
        }
    }
}

Last updated