Webhook Public Key API
Amwal provides a secured REST endpoint (/api/webhooks/public-key/) to programmatically retrieve, provision, and rotate the RSA 2048-bit public verification key used to validate incoming webhook signatures.
This endpoint is utilized by e-commerce plugins (such as WooCommerce and Magento), headless platforms, and automated CI/CD pipelines to manage webhook credentials without manual dashboard intervention.
Keep Credentials Confidential
The Authorization secret token and private signing keys must remain strictly confidential. Only invoke this endpoint from a secure server-to-server environment. Never expose your secret keys or execute this request from client-side / browser applications.
Endpoint Details
| Attribute | Details |
|---|---|
| Methods | GET, POST |
| Sandbox URL | https://backend.sa.amwal.tech/api/webhooks/public-key/ |
| Production URL | https://backend.sa.amwal.tech/api/webhooks/public-key/ |
| Authentication | Dual-header (Authorization + X-Amwal-Key) |
| Content-Type | application/json |
Authentication & Headers
Requests must provide merchant authentication credentials and environment context using the following headers:
| Header | Type | Required | Description |
|---|---|---|---|
Authorization | String | Yes | Confidential Merchant Secret Key or Auth Token (e.g., b2c8273d-xxxx-xxxx-xxxx-xxxxxxxxxxxx). |
X-Amwal-Key | String | Yes | Merchant Public Identifier (sandbox-amwal-xxxx for Sandbox or prod-amwal-xxxx for Production). |
Content-Type | String | Yes | Must be set to application/json. |
Accept | String | No | Recommended application/json. |
1. Retrieve Current Public Key & Endpoint Status (GET)
Use GET to inspect the currently registered RSA public key, key fingerprint, webhook listener endpoint configuration, delivery health statistics, and active event subscriptions.
Request
curl -s -X GET "https://backend.sa.amwal.tech/api/webhooks/public-key/" \
-H "Authorization: YOUR_MERCHANT_SECRET_TOKEN" \
-H "X-Amwal-Key: sandbox-amwal-c1edaa49-xxxx-xxxx-xxxx-xxxxxxxxxxxx" \
-H "Content-Type: application/json"Success Response (200 OK)
{
"merchant": {
"id": "f24267bd-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"name": "Your Store Name"
},
"api_key": {
"id": "399a018e-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"name": "Amwal Webhook Key",
"type": "sandbox",
"public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...\n-----END PUBLIC KEY-----",
"key_fingerprint": "6c1d480a214fa0c232768978e9a73c8e33ca13bbecfd5dcf607241048a32b58f",
"is_active": true,
"created_at": "2026-07-19T05:28:40.208569+00:00",
"last_used_at": "2026-10-29T10:31:41.140688+00:00"
},
"webhook_endpoint": {
"id": "cdeefc25-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"url": "https://yourdomain.com/wp-json/wc/amwal/v2/webhooks",
"description": "Amwal WooCommerce Plugin",
"is_active": true,
"is_global": false,
"created_at": "2026-07-19T05:28:40.200518+00:00",
"updated_at": "2026-09-10T08:46:43.242834+00:00",
"success_count": 1284,
"failure_count": 12,
"last_success": "2026-10-29T11:25:20.796150+00:00",
"last_failure": "2026-10-28T09:12:04.140688+00:00"
},
"webhook_events": [
"order.success",
"order.failed",
"order.updated",
"payment_link.expired",
"modal.closed"
]
}2. Provision or Rotate Public Key (POST)
Use POST to upload, register, or rotate an RSA 2048-bit public key. This is commonly executed during plugin onboarding or scheduled automated key rotations.
Request Body Parameters
| Field | Type | Required | Description |
|---|---|---|---|
public_key | String | Yes | RSA 2048-bit public key formatted as a PEM string with -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY----- delimiters. |
Example cURL Request
curl -s -X POST "https://backend.sa.amwal.tech/api/webhooks/public-key/" \
-H "Authorization: YOUR_MERCHANT_SECRET_TOKEN" \
-H "X-Amwal-Key: sandbox-amwal-c1edaa49-xxxx-xxxx-xxxx-xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...\n-----END PUBLIC KEY-----"
}'Success Response (200 OK)
The response returns the updated configuration and computed key_fingerprint:
{
"merchant": {
"id": "f24267bd-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"name": "Your Store Name"
},
"api_key": {
"id": "399a018e-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"name": "Amwal Webhook Key",
"type": "sandbox",
"public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...\n-----END PUBLIC KEY-----",
"key_fingerprint": "6c1d480a214fa0c232768978e9a73c8e33ca13bbecfd5dcf607241048a32b58f",
"is_active": true,
"created_at": "2026-10-29T11:30:00.000000+00:00",
"last_used_at": null
},
"webhook_endpoint": {
"id": "cdeefc25-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"url": "https://yourdomain.com/wp-json/wc/amwal/v2/webhooks",
"description": "Amwal WooCommerce Plugin",
"is_active": true,
"is_global": false,
"created_at": "2026-07-19T05:28:40.200518+00:00",
"updated_at": "2026-10-29T11:30:00.000000+00:00",
"success_count": 1284,
"failure_count": 12,
"last_success": "2026-10-29T11:25:20.796150+00:00",
"last_failure": "2026-10-28T09:12:04.140688+00:00"
},
"webhook_events": [
"order.success",
"order.failed",
"order.updated",
"payment_link.expired",
"modal.closed"
]
}Response Data Dictionary
1. merchant
| Field | Type | Description |
|---|---|---|
id | UUID String | Unique identifier of the merchant organization. |
name | String | Registered business or display name of the merchant. |
2. api_key
| Field | Type | Description |
|---|---|---|
id | UUID String | Unique identifier assigned to the webhook key record. |
name | String | Descriptive identifier of the key (e.g., plugin name or custom label). |
type | String | Active environment mode (sandbox or production). |
public_key | PEM String | The 2048-bit RSA public key string used to verify incoming X-Signature headers with RSA-PSS SHA-256. |
key_fingerprint | Hex String | SHA-256 fingerprint hash of the public key. Echoed in outgoing webhook dispatches via the X-Api-Key header to enable zero-downtime key rotation. |
is_active | Boolean | Indicates whether this key is actively used to sign webhook deliveries. |
created_at | ISO 8601 | UTC timestamp of key creation. |
last_used_at | ISO 8601 | null | Timestamp of the most recent signature generation using this keypair. |
3. webhook_endpoint
| Field | Type | Description |
|---|---|---|
id | UUID String | Unique identifier of the registered webhook listener. |
url | HTTPS URL | Destination URL where Amwal dispatches webhook events. |
description | String | Descriptive notes for the listener endpoint. |
is_active | Boolean | Whether webhook dispatching is enabled for this endpoint. |
is_global | Boolean | Whether this endpoint receives events across all sub-accounts. |
created_at | ISO 8601 | Initial registration timestamp of the endpoint. |
updated_at | ISO 8601 | Last modification timestamp of the endpoint settings. |
success_count | Integer | Total count of successfully acknowledged dispatches (2xx responses). |
failure_count | Integer | Total count of failed deliveries (connection timeouts, 4xx, or 5xx responses). |
last_success | ISO 8601 | null | Timestamp of the last successful dispatch delivery. |
last_failure | ISO 8601 | null | Timestamp of the last failed dispatch attempt. |
4. webhook_events
Array of event strings currently subscribed to by this endpoint. For full payload specifications, see Webhook Event Types.
order.success: Payment authorization and capture completed.order.failed: Payment authorization rejected or cancelled.order.updated: Transaction refunded or adjusted.payment_link.expired: Payment link reached expiration before completion.modal.closed: Customer dismissed checkout window without completing payment.
Error & Validation Handling
Review the possible HTTP error codes and validation responses:
1. 401 Unauthorized
Occurs when the Authorization header is missing, formatted incorrectly, or invalid.
{
"detail": "Authentication credentials were not provided."
}Or when an invalid or expired token is passed:
{
"detail": "Not Authenticated"
}2. 400 Bad Request — Missing Public Key
Returned when calling POST without including the required public_key field in the request body:
{
"public_key": [
"This field is required."
]
}3. 400 Bad Request — Invalid RSA PEM Format
Returned when the provided string is not a valid 2048-bit RSA public key in PEM format:
{
"public_key": [
"Invalid public key format. Must be a valid RSA 2048-bit PEM key."
]
}RSA Key Generation Requirement
The public key must use 2048-bit RSA encryption and adhere to the standard X.509 SubjectPublicKeyInfo PEM format starting with -----BEGIN PUBLIC KEY-----.
4. 403 Forbidden — Scope or Organization Mismatch
Returned if the X-Amwal-Key does not match the merchant organization bound to the Authorization secret token, or if the user lacks administrative permissions.
{
"detail": "You do not have permission to perform this action."
}5. 404 Not Found — Unconfigured Endpoint
Returned if a webhook listener URL has not yet been registered for the merchant account. Amwal requires a destination Webhook URL before keys can be provisioned.
{
"detail": "No webhook endpoint configured. Please register a Webhook URL first."
}Code Examples
Node.js (TypeScript)
import axios from 'axios';
interface WebhookKeyResponse {
merchant: { id: string; name: string };
api_key: {
id: string;
public_key: string;
key_fingerprint: string;
is_active: boolean;
};
webhook_endpoint: {
url: string;
success_count: number;
failure_count: number;
};
webhook_events: string[];
}
const AMWAL_SECRET = process.env.AMWAL_SECRET_KEY!;
const AMWAL_PUBLIC = process.env.AMWAL_MERCHANT_KEY!; // sandbox-amwal-xxx or prod-amwal-xxx
// 1. Fetch Current Public Key & Endpoint Health
async function getWebhookPublicKey(): Promise<WebhookKeyResponse> {
const response = await axios.get<WebhookKeyResponse>(
'https://backend.sa.amwal.tech/api/webhooks/public-key/',
{
headers: {
'Authorization': AMWAL_SECRET,
'X-Amwal-Key': AMWAL_PUBLIC,
'Content-Type': 'application/json',
},
}
);
return response.data;
}
// 2. Provision or Rotate Public Key
async function updateWebhookPublicKey(pemPublicKey: string): Promise<WebhookKeyResponse> {
const response = await axios.post<WebhookKeyResponse>(
'https://backend.sa.amwal.tech/api/webhooks/public-key/',
{ public_key: pemPublicKey },
{
headers: {
'Authorization': AMWAL_SECRET,
'X-Amwal-Key': AMWAL_PUBLIC,
'Content-Type': 'application/json',
},
}
);
return response.data;
}Python
import requests
import os
AMWAL_SECRET = os.getenv("AMWAL_SECRET_KEY")
AMWAL_PUBLIC = os.getenv("AMWAL_MERCHANT_KEY")
ENDPOINT_URL = "https://backend.sa.amwal.tech/api/webhooks/public-key/"
headers = {
"Authorization": AMWAL_SECRET,
"X-Amwal-Key": AMWAL_PUBLIC,
"Content-Type": "application/json"
}
# 1. Fetch Key & Endpoint Status
response = requests.get(ENDPOINT_URL, headers=headers)
if response.status_code == 200:
data = response.json()
print("Active Fingerprint:", data["api_key"]["key_fingerprint"])
print("Endpoint URL:", data["webhook_endpoint"]["url"])
print("Deliveries - Success:", data["webhook_endpoint"]["success_count"])
# 2. Provision New Public Key
new_key_pem = """-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
-----END PUBLIC KEY-----"""
update_resp = requests.post(ENDPOINT_URL, headers=headers, json={"public_key": new_key_pem})
if update_resp.status_code == 200:
print("Key updated successfully!")PHP
<?php
$secretKey = getenv('AMWAL_SECRET_KEY');
$merchantKey = getenv('AMWAL_MERCHANT_KEY');
$url = 'https://backend.sa.amwal.tech/api/webhooks/public-key/';
$headers = [
'Authorization: ' . $secretKey,
'X-Amwal-Key: ' . $merchantKey,
'Content-Type: application/json',
'Accept: application/json'
];
// 1. Get Webhook Key & Health Status
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 200) {
$data = json_decode($response, true);
echo "Webhook URL: " . $data['webhook_endpoint']['url'] . "\n";
echo "Key Fingerprint: " . $data['api_key']['key_fingerprint'] . "\n";
}Zero-Downtime Key Rotation Workflow
When rotating keys in production, backends can support dual validation without dropping dispatches:
- Deploy New Key to Receiver: Update your webhook listener server to accept signatures from both the previous key and the new key, indexing keys by
key_fingerprint. - Call Provisioning Endpoint: Call
POST /api/webhooks/public-key/with your new 2048-bit RSA public key. - Verify Incoming Dispatches: Future webhook dispatches will carry the new key's fingerprint in the
X-Api-Keyheader. - Decommission Old Key: Once all in-flight dispatches have settled, retire the old key from your backend cache.
Related Documentation
Webhook Setup
Configure your Sandbox and Production webhook listener endpoints, generate RSA verification keys, and subscribe to events in the Amwal Merchant Portal.
Webhook Signature Verification
Cryptographic specification and multi-language implementation for validating Amwal RSA-PSS SHA-256 webhook signatures.
