> ## Documentation Index
> Fetch the complete documentation index at: https://docs.justpaid.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Webhooks setup and usage

## Overview

Webhooks are a way for your application to receive real-time notifications when events occur in your account.

When an event occurs, we send an HTTP POST request to the webhook's configured URL.
You can use webhooks to trigger custom code, workflows, or integrations in your application.

## Creating Webhooks

You can create and manage webhooks directly from the JustPaid dashboard:

1. Navigate to **Settings** → **Developer** → **Webhooks**
2. Click **Create Webhook URL**
3. Enter your webhook endpoint URL
4. Select the event types you want to subscribe to
5. Click **Save**

Each webhook includes a signing secret that you can use to verify the authenticity of incoming requests.

## Webhook Headers

Each webhook request includes the following headers for verification:

| Header                 | Description                                       |
| ---------------------- | ------------------------------------------------- |
| `X-JustPaid-Timestamp` | Unix timestamp when the webhook was sent          |
| `X-JustPaid-Signature` | HMAC-SHA256 signature in format `v1={sha256_hex}` |

### Signature Verification

To verify a webhook signature:

1. Get the raw request body
2. Concatenate the timestamp and body: `{timestamp}.{body}`
3. Compute HMAC-SHA256 using your webhook signing secret
4. Compare with the provided signature

```python theme={null}
import hmac
import hashlib

def verify_justpaid_webhook(payload_body: bytes, timestamp: str, signature: str, secret: str) -> bool:
    expected_sig = hmac.new(
        secret.encode('utf-8'),
        f"{timestamp}.{payload_body.decode('utf-8')}".encode('utf-8'),
        hashlib.sha256
    ).hexdigest()
    provided_sig = signature.replace("v1=", "")
    return hmac.compare_digest(expected_sig, provided_sig)
```

## Webhook Events

The following events can trigger a webhook:

| Event Type                      | Description                                 |
| ------------------------------- | ------------------------------------------- |
| `INVOICE_PAYMENT_STATUS_CHANGE` | Payment status changed on an invoice        |
| `INVOICE_STATUS_CHANGE`         | Invoice status changed (e.g., draft → sent) |
| `INVOICE_CREATED`               | A new invoice was created                   |
| `INVOICE_UPDATED`               | An existing invoice was updated             |
| `CUSTOMER_CREATED`              | A new customer was created                  |
| `CUSTOMER_CONTRACT_CREATED`     | A new contract was created for a customer   |
| `PRODUCT_CREATED`               | A new product was created                   |
| `PRODUCT_UPDATED`               | An existing product was updated             |
| `CREDIT_MEMO_CREATED`           | A new credit memo was created               |
| `CREDIT_MEMO_UPDATED`           | An existing credit memo was updated         |
| `CREDIT_MEMO_STATUS_CHANGE`     | Credit memo status changed                  |

***

### INVOICE\_PAYMENT\_STATUS\_CHANGE

Triggered when the payment status of an invoice changes.

**Possible statuses:** `SCHEDULED`, `PROCESSED`, `SENT`, `NEED_ACTION`, `COMPLETED`, `FAILED`, `CANCELLED`, `VOIDED`

```json theme={null}
{
  "id": "evt_abc123...",
  "object": "event",
  "api_version": "v1",
  "created": "2026-01-27T10:30:00.000000+00:00",
  "type": "INVOICE_PAYMENT_STATUS_CHANGE",
  "data": {
    "object": {
      "invoice_payment_uuid": "d0b1c4c0-4b1e-4b3e-8b4b-1f1d8b1d8b1d",
      "status": "COMPLETED"
    }
  }
}
```

***

### INVOICE\_STATUS\_CHANGE

Triggered when an invoice's status changes (e.g., from draft to sent, or sent to paid).

```json theme={null}
{
  "id": "evt_abc123...",
  "object": "event",
  "api_version": "v1",
  "created": "2026-01-27T10:30:00.000000+00:00",
  "type": "INVOICE_STATUS_CHANGE",
  "data": {
    "object": {
      "uuid": "inv_123...",
      "status": "SENT",
      "previous_status": "DRAFT"
    }
  }
}
```

***

### INVOICE\_CREATED

Triggered when a new invoice is created.

```json theme={null}
{
  "id": "evt_abc123...",
  "object": "event",
  "api_version": "v1",
  "created": "2026-01-27T10:30:00.000000+00:00",
  "type": "INVOICE_CREATED",
  "data": {
    "object": {
      "uuid": "inv_123...",
      "invoice_number": "INV-2026-001",
      "customer_uuid": "cust_456...",
      "total_amount": 1500.00,
      "currency": "USD",
      "status": "DRAFT"
    }
  }
}
```

***

### INVOICE\_UPDATED

Triggered when an existing invoice is updated. This includes changes to line items, amounts, dates, or any other invoice fields.

```json theme={null}
{
  "id": "evt_abc123...",
  "object": "event",
  "api_version": "v1",
  "created": "2026-01-27T10:30:00.000000+00:00",
  "type": "INVOICE_UPDATED",
  "data": {
    "object": {
      "uuid": "inv_123...",
      "invoice_number": "INV-2026-001",
      "customer_uuid": "cust_456...",
      "total_amount": 1750.00,
      "currency": "USD",
      "status": "DRAFT"
    }
  }
}
```

***

### CUSTOMER\_CREATED

Triggered when a new customer is created.

```json theme={null}
{
  "id": "evt_abc123...",
  "object": "event",
  "api_version": "v1",
  "created": "2026-01-27T10:30:00.000000+00:00",
  "type": "CUSTOMER_CREATED",
  "data": {
    "object": {
      "uuid": "cust_456...",
      "name": "Acme Corporation",
      "email": "billing@acme.com"
    }
  }
}
```

***

### CUSTOMER\_CONTRACT\_CREATED

Triggered when a new contract is created for a customer.

```json theme={null}
{
  "id": "evt_abc123...",
  "object": "event",
  "api_version": "v1",
  "created": "2026-01-27T10:30:00.000000+00:00",
  "type": "CUSTOMER_CONTRACT_CREATED",
  "data": {
    "object": {
      "uuid": "contract_789...",
      "customer_uuid": "cust_456...",
      "name": "Enterprise License Agreement",
      "start_date": "2026-01-01",
      "end_date": "2026-12-31"
    }
  }
}
```

***

### PRODUCT\_CREATED

Triggered when a new product is created.

```json theme={null}
{
  "id": "evt_abc123...",
  "object": "event",
  "api_version": "v1",
  "created": "2026-01-27T10:30:00.000000+00:00",
  "type": "PRODUCT_CREATED",
  "data": {
    "object": {
      "uuid": "prod_abc...",
      "name": "Enterprise Plan",
      "category": "Software",
      "billing_alias": "ENT-001",
      "accounting_item": {
        "uuid": "acc_xyz...",
        "name": "Software Revenue",
        "external_id": "4000"
      }
    }
  }
}
```

***

### PRODUCT\_UPDATED

Triggered when an existing product is updated.

```json theme={null}
{
  "id": "evt_abc123...",
  "object": "event",
  "api_version": "v1",
  "created": "2026-01-27T10:30:00.000000+00:00",
  "type": "PRODUCT_UPDATED",
  "data": {
    "object": {
      "uuid": "prod_abc...",
      "name": "Enterprise Plan Pro",
      "category": "Software",
      "billing_alias": "ENT-002"
    }
  }
}
```

***

### CREDIT\_MEMO\_CREATED

Triggered when a new credit memo is created. Credit memos are used to handle refunds and credits.

```json theme={null}
{
  "id": "evt_abc123...",
  "object": "event",
  "api_version": "v1",
  "created": "2026-01-27T10:30:00.000000+00:00",
  "type": "CREDIT_MEMO_CREATED",
  "data": {
    "object": {
      "uuid": "cm_123...",
      "customer_uuid": "cust_456...",
      "amount": 500.00,
      "currency": "USD",
      "reason": "Service credit",
      "status": "DRAFT"
    }
  }
}
```

***

### CREDIT\_MEMO\_UPDATED

Triggered when an existing credit memo is updated.

```json theme={null}
{
  "id": "evt_abc123...",
  "object": "event",
  "api_version": "v1",
  "created": "2026-01-27T10:30:00.000000+00:00",
  "type": "CREDIT_MEMO_UPDATED",
  "data": {
    "object": {
      "uuid": "cm_123...",
      "customer_uuid": "cust_456...",
      "amount": 600.00,
      "currency": "USD",
      "reason": "Updated service credit",
      "status": "DRAFT"
    }
  }
}
```

***

### CREDIT\_MEMO\_STATUS\_CHANGE

Triggered when a credit memo's status changes.

```json theme={null}
{
  "id": "evt_abc123...",
  "object": "event",
  "api_version": "v1",
  "created": "2026-01-27T10:30:00.000000+00:00",
  "type": "CREDIT_MEMO_STATUS_CHANGE",
  "data": {
    "object": {
      "uuid": "cm_123...",
      "status": "APPLIED",
      "previous_status": "DRAFT"
    }
  }
}
```

## Delivery

* Webhooks are delivered asynchronously
* Automatic retries on failure (up to 3 attempts with exponential backoff)
* Each event has a unique `id` for idempotency
* Events are delivered in order when possible, but you should handle out-of-order delivery

## Best Practices

1. **Respond quickly** - Return a 2xx status code within 30 seconds
2. **Process asynchronously** - Queue webhook events for background processing
3. **Verify signatures** - Always validate the `X-JustPaid-Signature` header
4. **Handle duplicates** - Use the event `id` for idempotency
5. **Log events** - Keep records for debugging and auditing
