# Manage webhooks

## Create a webhook

Permissions required
Only owners and administrators can configure webhooks.

1. Develop a HTTPS endpoint on your server to receive webhook events
2. Navigate to the ‘Settings’ page in the Wallet-as-a-Service (Palisade) console and click ‘Webhooks’
3. Click ‘Create webhook’
4. Enter a webhook name
This should be an identifiable name that describes the purpose of the webhook.
5. Optional: enter a description for the webhook
6. Copy and paste the HTTPS endpoint address into the ‘URL’ section
7. Select which events you want the endpoint to receive
8. Click ‘Save’


## Verify webhook deliveries

1. After you create a webhook, Wallet-as-a-Service (Palisade) generates and returns a P-256 ECDSA public key in ASN.1 DER format.
For example:



```json
{
  "id": "a6a1e8bd-30eb-419f-b6ba-e7ac11e86f84",
  "name": "wefewfwe",
  "publicKey": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEpHiEkdd/ba5dU4sNCgsWrMOE6fLakHywl0OgF5aVfxkiqNh22oybRAREev7jvnwH4jqitHx79KGi6CMwiJjmaw==",
  // other fields omitted
}
```

You can view this public key by clicking on the webhook in the Wallet-as-a-Service (Palisade) console.

1. Extract the signature from the webhook event headers in the URL (it will be in ASN.1 DER format, base64-encoded)
2. Create a SHA-256 hash of the base64-encoded payload
3. Use the public key to verify that the signature matches the payload hash


Most programming languages have cryptography libraries that support ECDSA verification.

### Code Examples

#### Python


```python
import hashlib
import base64
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.serialization import load_der_public_key

def verify_webhook(public_key_b64: str, signature_b64: str, payload_b64: str) -> bool:
    """
    Verify a Palisade webhook signature.

    Args:
        public_key_b64: Base64-encoded public key from webhook creation
        signature_b64: Base64-encoded signature from the 'Signature' header
        payload_b64: The base64-encoded payload string from the webhook body

    Returns:
        True if signature is valid, False otherwise
    """
    public_key = load_der_public_key(base64.b64decode(public_key_b64))
    signature = base64.b64decode(signature_b64)
    payload_hash = hashlib.sha256(payload_b64.encode()).digest()

    try:
        public_key.verify(signature, payload_hash, ec.ECDSA(hashes.SHA256()))
        return True
    except Exception:
        return False
```

#### JavaScript (Node.js)


```javascript
const crypto = require('crypto');

/**
 * Verify a Palisade webhook signature.
 *
 * @param {string} publicKeyB64 - Base64-encoded public key from webhook creation
 * @param {string} signatureB64 - Base64-encoded signature from the 'Signature' header
 * @param {string} payloadB64 - The base64-encoded payload string from the webhook body
 * @returns {boolean} - True if signature is valid
 */
function verifyWebhook(publicKeyB64, signatureB64, payloadB64) {
  const publicKey = crypto.createPublicKey({
    key: Buffer.from(publicKeyB64, 'base64'),
    format: 'der',
    type: 'spki'
  });

  const payloadHash = crypto.createHash('sha256').update(payloadB64).digest();

  return crypto.verify(
    null,
    payloadHash,
    publicKey,
    Buffer.from(signatureB64, 'base64')
  );
}
```

#### Go


```go
package main

import (
	"crypto/ecdsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
)

// VerifyWebhook verifies a Palisade webhook signature.
func VerifyWebhook(publicKeyB64, signatureB64, payloadB64 string) (bool, error) {
	publicKeyDER, err := base64.StdEncoding.DecodeString(publicKeyB64)
	if err != nil {
		return false, err
	}

	pubKey, err := x509.ParsePKIXPublicKey(publicKeyDER)
	if err != nil {
		return false, err
	}

	ecdsaPubKey, ok := pubKey.(*ecdsa.PublicKey)
	if !ok {
		return false, err
	}

	signature, err := base64.StdEncoding.DecodeString(signatureB64)
	if err != nil {
		return false, err
	}

	hash := sha256.Sum256([]byte(payloadB64))

	return ecdsa.VerifyASN1(ecdsaPubKey, hash[:], signature), nil
}
```

## Security Requirements & Best Practices

* **HTTPS Only**: All webhook endpoints must use HTTPS for secure communication
* **Signature Verification**: Always verify the webhook signature to ensure authenticity
* **Response Codes**: Return a 200 OK response code to acknowledge receipt of the webhook


## Webhook settings

The console lists your webhooks in a table after you create them.

You can view or delete webhooks by clicking the three dots in the ‘actions’ column of the table.

When you view a webhook, you see the following information:

* Name
* Description
* URL
* Public key
* Subscriptions: the event subscriptions configured
* Who created the webhook and when


API documentation
See our [Wallet-as-a-Service (Palisade) API reference](/products/wallet/api-docs/palisade-api/palisade-api) for information on how to configure webhooks via the API.