---
updatedAt: 2026-02-24T23:23:25.000Z
agentTools:
  projectIndex: https://docs.pelcro.com/v2.4/llms.txt
---

# Webhooks

Learn how to receive real-time notifications when events occur in your Pelcro account.

## Quickstart

Get up and running with Pelcro webhooks in three steps:

**1. Create an endpoint** that accepts POST requests and returns a `200` status code:

```php PHP
// PHP
// Laravel
Route::post('/webhooks/pelcro', function (Request $request) {
    $event = $request->all();
    Log::info("Received {$event['type']} event: {$event['id']}");

    // Acknowledge receipt immediately
    // Process the event asynchronously (e.g., dispatch a queued job)
    ProcessWebhookEvent::dispatch($event);

    return response('OK', 200);
});
```
```python Python
# Flask
@app.route('/webhooks/pelcro', methods=['POST'])
def handle_webhook():
    event = request.get_json()
    print(f"Received {event['type']} event: {event['id']}")

    # Acknowledge receipt immediately
    # Process the event asynchronously (e.g., enqueue a task)
    process_event.delay(event)

    return 'OK', 200
```
```javascript Node.js
// Express
app.post('/webhooks/pelcro', (req, res) => {
  const event = req.body;
  console.log(`Received ${event.type} event: ${event.id}`);

  // Acknowledge receipt immediately
  res.status(200).send('OK');

  // Process the event asynchronously
  processEvent(event);
});
```

**2. Register your URL** in the Pelcro Platform under **Settings > Notifications**. Create a new webhook endpoint, enter your URL, then select the events you want to receive.

**3. Verify signatures** to confirm events are from Pelcro (see [Signature Verification](#signature-verification) below).

That's it. Pelcro will now send an HTTP POST request to your endpoint every time a subscribed event occurs.

***

## Configuration

You can register webhook endpoints from **Settings > Notifications** in the Pelcro Platform. Although you can subscribe to all events, this is not recommended as it will put unnecessary pressure on your server. Configure your webhook to listen to only the events required by your integration. You can also subscribe to webhooks via the API.

When registering the URL, it must respond with successful status code (2xx) to the POST requests in order to pass the initial validation.

Once you register a webhook URL with Pelcro, we will issue an HTTP POST request to the URL specified every time that event occurs, and your endpoint is expected to answer in a timely fashion by returning a successful status code (2xx). Any other status code is considered a failure.

## Payload Structure

Every webhook event follows the same envelope structure:

```json
{
    "type": "subscription.created",
    "id": "evt_ybR5i5QwSVvoWy28ndD4q6hA",
    "created": 1542034347,
    "data": {
        "object": {
            "object": "subscription",
            "id": 6807,
            "user_id": 40864,
            "plan_id": 386,
            "cancel_at_period_end": false,
            "canceled_at": null,
            "current_period_end": "2024-02-01T00:00:00.000000Z",
            "current_period_start": "2024-01-01T00:00:00.000000Z"
        }
    }
}
```

| Field         | Type    | Description                                                                                                                                                              |
| :------------ | :------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type`        | string  | The event type (e.g., `subscription.created`).                                                                                                                           |
| `id`          | string  | A unique event identifier (format: `evt_` + 24 characters). Use this for idempotency — if your endpoint receives the same `id` twice, you can safely skip the duplicate. |
| `created`     | integer | Unix timestamp of when the event was created.                                                                                                                            |
| `data.object` | object  | The resource that triggered the event. The `object` field inside identifies the resource type.                                                                           |

For events that include change tracking (e.g., `subscription.changed`, `customer.updated`), the payload also includes a `data.previous_attributes` object with the field values before the change.

The webhook is sent with the following headers:

```
Content-Type: application/json
X-Pelcro-Hmac-SHA256: pdvcCMQA5298lNhqp7i52StRjHEKgY7Z77yU+f+lfGQ=
```

## Events Ordering

The order in which events are delivered to endpoints is not related to the order they were triggered on our system. For example, the logical flow of events triggered while creating a subscription might be the following:

* subscription.created
* invoice.created
* charge.created

Your integration shouldn't assume delivery of events in the order specified in the example above, and needs to handle processing accordingly.

***

## Delivery and Retries

### Timeouts

Your endpoint must respond quickly. Pelcro enforces the following timeouts:

| Description                                                             | Timeout    |
| :---------------------------------------------------------------------- | :--------- |
| Maximum time for your endpoint to accept the connection                 | 5 seconds  |
| Maximum time for your endpoint to return a successful status code (2xx) | 20 seconds |

Your endpoint should acknowledge receipt of the event immediately by returning a `2xx` status code, then process the event asynchronously in a background job or queue. Do not perform business logic (API calls, database operations, emails) before responding.

### Retry Behavior

If your endpoint fails to return a `2xx` status code, Pelcro will retry the delivery up to **3 attempts**. Retries are processed immediately through our queue system with no delay between attempts. After all attempts are exhausted, the delivery is marked as permanently failed.

> 🚧 **No exponential backoff**
>
> Retries happen in quick succession. If your endpoint is temporarily down, all 3 attempts may fail before it recovers. Make sure your endpoint is highly available, and monitor your webhook delivery statuses in the Pelcro Platform.

### Statuses

Various circumstances can cause webhook event delivery to enter a specific status. Refer to the table below for information on each status.

| Webhook status          | Description                                                                                                                                                                                                                                                                                                                      |
| :---------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| OK                      | The endpoint returned a successful status code (2xx).                                                                                                                                                                                                                                                                            |
| ERR - 3xx               | The endpoint attempted to redirect the request to another location, which is not handled and thus is considered a failure.                                                                                                                                                                                                       |
| ERR - 4xx               | The endpoint can't or won't process the request. This might occur when the server detects an error (400), when it has access restrictions (401, 403), or when the endpoint URL doesn't exist (404).                                                                                                                              |
| ERR - 5xx               | The endpoint encountered an error while processing the request.                                                                                                                                                                                                                                                                  |
| ERR - TLS               | A secure connection could not be established to the endpoint. These errors can be caused by an issue with the SSL/TLS certificate or an intermediate certificate in the destination server's certificate chain. Performing a [SSL server test](https://www.ssllabs.com/ssltest/) might help identifying the cause of this error. |
| ERR - Unable to connect | A connection with the endpoint could not be established. This might happen if the endpoint cannot be resolved, or if the destination server did not accept the connection in a timely manner.                                                                                                                                    |
| ERR - Timed out         | The endpoint did not send back a successful status code (2xx) in a timely manner.                                                                                                                                                                                                                                                |
| ERR                     | An unspecified, unhandled or unrecognized error occurred.                                                                                                                                                                                                                                                                        |

### Best Practices

* **Return 2xx immediately**, then process events in a background queue.
* **Use the `id` field for idempotency.** Your endpoint may receive the same event more than once. Store processed event IDs and skip duplicates.
* **Subscribe only to events you need.** This reduces load on your server and simplifies your integration.
* **Monitor delivery statuses** in the Pelcro Platform to catch endpoint failures early.

***

## Signature Verification

Pelcro signs every webhook event by including an HMAC-SHA256 signature in the `X-Pelcro-Hmac-SHA256` header. This allows you to verify that events were sent by Pelcro, not by a third party.

### How it works

1. **Extract** the base64-encoded signature from the `X-Pelcro-Hmac-SHA256` header.
2. **Compute** an HMAC-SHA256 hash using your webhook signing secret as the key and the raw JSON request body as the message.
3. **Compare** your computed signature with the one from the header.
4. **Return** a `2xx` status code to acknowledge receipt. Pelcro will retry up to 3 times on failure.

### Code Examples

```php PHP
// PHP

$payload = file_get_contents('php://input');
$secret  = 'your_webhook_secret';

$expected = base64_encode(
    hash_hmac('sha256', $payload, $secret, true)
);

$signature = $_SERVER['HTTP_X_PELCRO_HMAC_SHA256'] ?? '';

if (!hash_equals($expected, $signature)) {
    http_response_code(401);
    exit('Invalid signature');
}

http_response_code(200);

// Process the event
$event = json_decode($payload, true);
```
```javascript Node.js
const crypto = require('crypto');

app.post('/webhooks/pelcro', (req, res) => {
  const payload   = JSON.stringify(req.body);
  const secret    = 'your_webhook_secret';
  const signature = req.headers['x-pelcro-hmac-sha256'];

  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('base64');

  if (expected !== signature) {
    return res.status(401).send('Invalid signature');
  }

  res.status(200).send('OK');

  // Process the event
  const event = req.body;
});
```
```python Python
import hmac
import hashlib
import base64

payload   = request.get_data()  # raw request body
secret    = b'your_webhook_secret'
signature = request.headers.get('X-Pelcro-Hmac-SHA256', '')

expected = base64.b64encode(
    hmac.new(secret, payload, hashlib.sha256).digest()
).decode()

if not hmac.compare_digest(expected, signature):
    abort(401, 'Invalid signature')

return 'OK', 200
```
```ruby Ruby
require 'openssl'
require 'base64'

payload   = request.body.read
secret    = 'your_webhook_secret'
signature = request.env['HTTP_X_PELCRO_HMAC_SHA256']

expected = Base64.strict_encode64(
  OpenSSL::HMAC.digest('sha256', secret, payload)
)

unless Rack::Utils.secure_compare(expected, signature)
  halt 401, 'Invalid signature'
end

status 200
```

> 🚧 **Use constant-time comparison**
>
> Always use a constant-time string comparison function (like `hash_equals` in PHP, `hmac.compare_digest` in Python, or `Rack::Utils.secure_compare` in Ruby) to prevent timing attacks.

***

## Events

Below is a complete list of all events we currently send to your configured webhook endpoint. Select an event's resource page to see the full JSON payload. Please note that we may change, delete, or add more at any time. While developing and maintaining your code, keep an eye on the [changelog](https://docs.pelcro.com/changelog).

### [Addresses](/docs/webhooks-addresses)

| Event           | Description                                                         |
| :-------------- | :------------------------------------------------------------------ |
| address.created | A new address is created and added to a customer's account.         |
| address.updated | An existing address is updated or modified on a customer's account. |

### [Bills](/docs/webhooks-bills)

| Event          | Description                                         |
| :------------- | :-------------------------------------------------- |
| bill.created   | A new bill is created for a vendor.                 |
| bill.finalized | A draft bill is finalized and moved to open status. |
| bill.paid      | A bill is marked as paid.                           |
| bill.updated   | A bill is updated.                                  |
| bill.deleted   | A bill is deleted.                                  |

### [Campaigns](/docs/webhooks-campaigns)

| Event                   | Description                                                         |
| :---------------------- | :------------------------------------------------------------------ |
| campaign.triggered      | A campaign with a notification action has started processing.       |
| campaign.run\_completed | A campaign run has completed processing all matching subscriptions. |

### [Charges](/docs/webhooks-charges)

| Event            | Description                                                 |
| :--------------- | :---------------------------------------------------------- |
| charge.failed    | A charge attempt on a customer's payment method has failed. |
| charge.refunded  | A charge has been refunded (fully or partially).            |
| charge.succeeded | A customer's payment method has been successfully charged.  |

### [Customers](/docs/webhooks-customers)

| Event                                   | Description                                                              |
| :-------------------------------------- | :----------------------------------------------------------------------- |
| customer.created                        | A new customer registers and a user record is created.                   |
| customer.updated                        | A customer's profile information is updated.                             |
| customer.deleted                        | A customer record is deleted.                                            |
| customer.PasswordResetRequestSubmitted  | A customer requests a password reset. Contains the password reset token. |
| customer.password\_update.succeeded     | A customer successfully updates their password.                          |
| customer.email\_verification\_request   | A customer is requested to verify their email after account creation.    |
| customer.email\_verification\_succeeded | A customer successfully verifies their email.                            |
| customer.passwordless\_login\_request   | A customer requests to log in via passwordless login.                    |

### [Exports](/docs/webhooks-exports)

| Event             | Description                                                 |
| :---------------- | :---------------------------------------------------------- |
| export.requested  | A data export job was submitted and processing has started. |
| export.completed  | A data export has been generated and is ready for download. |
| export.downloaded | A data export file has been downloaded.                     |
| export.failed     | A data export has failed to generate.                       |

### [Fraud Prevention](/docs/webhooks-fraud-prevention)

| Event                       | Description                                                      |
| :-------------------------- | :--------------------------------------------------------------- |
| fraud\_prevention.triggered | A fraud prevention rule with a Notify action has been triggered. |

### [Invoices](/docs/webhooks-invoices)

| Event                             | Description                                                                            |
| :-------------------------------- | :------------------------------------------------------------------------------------- |
| invoice.created                   | An invoice is created (e.g., when a subscription is created or renewed).               |
| invoice.payment\_action\_required | An invoice payment attempt requires further customer action to complete.               |
| invoice.payment\_failed           | An invoice payment attempt fails.                                                      |
| invoice.payment\_succeeded        | An invoice payment attempt succeeds.                                                   |
| invoice.upcoming                  | The configured number of days before an invoice is scheduled to be created is reached. |
| invoice.updated                   | An invoice is updated.                                                                 |

### [Members](/docs/webhooks-members)

| Event                        | Description                                                                    |
| :--------------------------- | :----------------------------------------------------------------------------- |
| member.activated             | A member activates their membership under a subscription they were invited to. |
| member.created               | A member is invited to join a subscription.                                    |
| member.updated               | A member's information is updated.                                             |
| member.deleted               | A member is removed from a subscription.                                       |
| member.subscription\_updated | The subscription associated with a member is updated.                          |

### [Newsletters](/docs/webhooks-newsletters)

| Event              | Description                                          |
| :----------------- | :--------------------------------------------------- |
| newsletter.created | A newsletter subscription is created for a customer. |
| newsletter.updated | A newsletter subscription is updated for a customer. |

### [Orders](/docs/webhooks-orders)

| Event                   | Description                                 |
| :---------------------- | :------------------------------------------ |
| order.created           | A new e-commerce order is created.          |
| order.payment.succeeded | An order payment is successfully processed. |
| order.payment.failed    | An order payment attempt fails.             |

### [Payment Methods](/docs/webhooks-payment-methods)

| Event           | Description                                             |
| :-------------- | :------------------------------------------------------ |
| source.created  | A new payment method is added to a customer's account.  |
| source.updated  | A customer's payment method is updated.                 |
| source.expired  | A customer's payment method has expired.                |
| source.expiring | A customer's payment method is expiring within 30 days. |

### [Plans](/docs/webhooks-plans)

| Event        | Description                         |
| :----------- | :---------------------------------- |
| plan.created | A new subscription plan is created. |
| plan.updated | A subscription plan is updated.     |

### [Products](/docs/webhooks-products)

| Event                | Description                             |
| :------------------- | :-------------------------------------- |
| product.created      | A new product is created.               |
| product.updated      | A product is updated.                   |
| product.deleted      | A product is deleted.                   |
| product\_sku.created | A new product SKU (variant) is created. |
| product\_sku.updated | A product SKU is updated.               |
| product\_sku.deleted | A product SKU is deleted.               |

### [Subscriptions](/docs/webhooks-subscriptions)

| Event                           | Description                                                                                                                           |
| :------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------ |
| subscription.created            | A new subscription is created.                                                                                                        |
| subscription.updated            | A subscription is updated or transitions between statuses.                                                                            |
| subscription.changed            | A subscription is [changed](https://docs.pelcro.com/docs/subscriptions#changing-a-subscription) (plan or billing cycle modification). |
| subscription.canceled           | A subscription is canceled.                                                                                                           |
| subscription.expired            | A subscription has expired.                                                                                                           |
| subscription.renewed            | A subscription renews and a new billing period begins.                                                                                |
| subscription.trial\_will\_end   | A subscription's trial period is ending within three days, or a trial is ended immediately.                                           |
| subscription.gift\_notification | It's time to notify the gift recipient, according to the gift date set during creation.                                               |

### [Vendors](/docs/webhooks-vendors)

| Event          | Description                    |
| :------------- | :----------------------------- |
| vendor.created | A new vendor is created.       |
| vendor.updated | A vendor's profile is updated. |
| vendor.deleted | A vendor is deleted.           |

## Security

For added security, your integration should make sure the events it receives originate from Pelcro's infrastructure. See the list of [outgoing IP addresses](https://docs.pelcro.com/docs/security-overview#outgoing-ip-addresses) to whitelist.

# Sub pages

* [Email Templates](https://docs.pelcro.com/v2.4/docs/e-mail-templates-for-webhooks.md)
* [Addresses](https://docs.pelcro.com/v2.4/docs/webhooks-addresses.md)
* [Bills](https://docs.pelcro.com/v2.4/docs/webhooks-bills.md)
* [Campaigns](https://docs.pelcro.com/v2.4/docs/webhooks-campaigns.md)
* [Charges](https://docs.pelcro.com/v2.4/docs/webhooks-charges.md)
* [Customers](https://docs.pelcro.com/v2.4/docs/webhooks-customers.md)
* [Exports](https://docs.pelcro.com/v2.4/docs/webhooks-exports.md)
* [Fraud Prevention](https://docs.pelcro.com/v2.4/docs/webhooks-fraud-prevention.md)
* [Invoices](https://docs.pelcro.com/v2.4/docs/webhooks-invoices.md)
* [Members](https://docs.pelcro.com/v2.4/docs/webhooks-members.md)
* [Newsletters](https://docs.pelcro.com/v2.4/docs/webhooks-newsletters.md)
* [Orders](https://docs.pelcro.com/v2.4/docs/webhooks-orders.md)
* [Payment Methods](https://docs.pelcro.com/v2.4/docs/webhooks-payment-methods.md)
* [Plans](https://docs.pelcro.com/v2.4/docs/webhooks-plans.md)
* [Products](https://docs.pelcro.com/v2.4/docs/webhooks-products.md)
* [Subscriptions](https://docs.pelcro.com/v2.4/docs/webhooks-subscriptions.md)
* [Vendors](https://docs.pelcro.com/v2.4/docs/webhooks-vendors.md)

# Sibling pages

* [Model Context Protocol (MCP)](https://docs.pelcro.com/v2.4/docs/mcp.md)
* [JavaScript SDK](https://docs.pelcro.com/v2.4/docs/js-sdk.md)
* [iOS SDK](https://docs.pelcro.com/v2.4/docs/ios-sdk.md)
* [Android SDK](https://docs.pelcro.com/v2.4/docs/android-sdk.md)