Webhooks

React to events in your packages and automate workflows with webhooks.

Overview

Webhooks allow you to receive near real-time notifications when certain events occur in your packages.

You can use them to automate tasks like triggering custom workflows when a new version of your package is published, or when somebody purchases a license for a package.

Webhooks are available to Privato users on the Growth and Scale plans.

Events

You can create webhooks for the following events:

  • license.created – Fired when a license is created.
  • license.updated – Fired when a license is updated.
  • license.deleted – Fired when a license is deleted.
  • release.published – Fired when a new release is published.
  • package.created – Fired when a new package is created.
  • package.updated – Fired when a package is updated.
  • package.deleted – Fired when a package is deleted.
  • customer.created – Fired when a new customer is created.
  • customer.updated – Fired when a customer is updated.
  • package.downloaded – Fired when a package is downloaded.

Creating a webhook

To create a webhook, navigate to the "Webhooks" section inside of the Privato dashboard and click on the "Create webhook" button.

Each webhook must have a name, destination URL, and at least 1 event associated with it.

The destination URL is the URL that will receive the webhook payload when an event occurs. The request that Privato sends will be a POST request with a JSON body containing details about the event that occurred, e.g. the package created, the license updated, etc.

Securing your webhooks

All webhooks sent from Privato are sent with a unique "signing secret" inside of the X-Signature header. This can be used to verify that the incoming request is actually from Privato and not from a malicious third party.

After creating your webhook, you can find the signing secret by viewing the webhook details in the Privato dashboard. You should then use this to secure your webhook endpoint by verifying the signature received in the X-Signature header against the payload of the request using the signing secret.

Here's an example of how to verify the signature using PHP inside of an imaginary Laravel application.

public function __invoke(Request $request)
{
    $secret = config('services.privato.signing_secret');
    $payload = $request->getContent();
    $hash = hash_hmac('sha256', $payload, $secret);

    if (! hash_equals($hash, $request->header('X-Signature'))) {
        abort(401);
    }
}
Copy to clipboard

Payloads

Each event has a specific payload structure that is sent in the body of the webhook request.

License

{
    "id": 1,
    "key": "ABC123-DEF456-GHI789",
    "short_key": "ABC123",
    "created_at": "2024-01-01T00:00:00Z",
    "updated_at": "2024-01-01T00:00:00Z",
    "package": {
        "id": 1,
        "name": "Example Package",
        "slug": "example-package",
        "description": "This is an example package.",
        "repository": "vendor/package",
        "composer_name": "vendor/package",
        "created_at": "2024-01-01T00:00:00Z",
        "updated_at": "2024-01-01T00:00:00Z"
    },
    "customer": {
        "id": 1,
        "name": "John Doe",
        "email": "[email protected]",
        "marketing_consent": true,
        "created_at": "2024-01-01T00:00:00Z",
        "updated_at": "2024-01-01T00:00:00Z"
    }
}
Copy to clipboard

Customer

{
    "id": 1,
    "name": "John Doe",
    "email": "[email protected]",
    "marketing_consent": true,
    "created_at": "2024-01-01T00:00:00Z",
    "updated_at": "2024-01-01T00:00:00Z"
}
Copy to clipboard

Package

{
    "id": 1,
    "name": "Example Package",
    "slug": "example-package",
    "description": "This is an example package.",
    "repository": "vendor/package",
    "composer_name": "vendor/package",
    "created_at": "2024-01-01T00:00:00Z",
    "updated_at": "2024-01-01T00:00:00Z"
}
Copy to clipboard

Release

{
    "id": 1,
    "name": "v1.0.0",
    "body": "Initial release",
    "number_of_files": 2,
    "size_in_bytes": 1024,
    "published_at": "2024-01-01T00:00:00Z",
    "created_at": "2024-01-01T00:00:00Z",
    "updated_at": "2024-01-01T00:00:00Z",
    "package": {
        "id": 1,
        "name": "Example Package",
        "slug": "example-package",
        "description": "This is an example package.",
        "repository": "vendor/package",
        "composer_name": "vendor/package",
        "created_at": "2024-01-01T00:00:00Z",
        "updated_at": "2024-01-01T00:00:00Z"
    }
}
Copy to clipboard

Failed deliveries

If a webhook delivery fails because the destination URL did not respond with a 2xx code, Privato will automatically retry the delivery.

The first retry will be performed 10 seconds after the initial failed attempt, and the second retry will be performed 100 seconds after that.

If the second retry also fails, the webhook delivery will completely fail and Privato will stop trying to deliver that webhook.