# Note: You need to encrypt the payload first using a script/tool
# See the Encryption Guide for JWE token creation
# 1. Create encrypted JWE token (using external tool/script)
ENCRYPTED_TOKEN="eyJhbGciOiJBMjU2S1ciL..."
# 2. Make API request
curl -X POST https://webhooks.sbx.moduluslabs.io/v1/webhooks \
-u sk_your_secret_key: \
-H "Content-Type: application/json" \
-d "{\"request\": {\"Token\": \"$ENCRYPTED_TOKEN\"}}"
# 3. Response contains the created webhook object
const axios = require('axios');
const jose = require('jose');
const SECRET_KEY = process.env.MODULUS_SECRET_KEY;
const ENCRYPTION_KEY = process.env.MODULUS_ENCRYPTION_KEY;
async function createWebhook() {
// 1. Prepare the payload
const payload = {
webhookAction: 'QRPH_SUCCESS',
callbackUrl: 'https://api.yourcompany.com/webhooks/success'
};
// 2. Encrypt payload into JWE token
const key = Buffer.from(ENCRYPTION_KEY, 'base64');
const jweToken = await new jose.CompactEncrypt(
new TextEncoder().encode(JSON.stringify(payload))
)
.setProtectedHeader({ alg: 'A256KW', enc: 'A256CBC-HS512' })
.encrypt(key);
// 3. Make API request
const response = await axios.post(
'https://webhooks.sbx.moduluslabs.io/v1/webhooks',
{ request: { Token: jweToken } },
{
auth: { username: SECRET_KEY, password: '' },
headers: { 'Content-Type': 'application/json' }
}
);
const result = response.data;
console.log('Webhook created successfully');
console.log('Webhook ID:', result.id);
console.log('Webhook URL:', result.callbackUrl);
console.log('Action:', result.webhookAction);
console.log('Status:', result.webhookStatus);
return result;
}
createWebhook()
.catch(error => {
console.error('Error:', error.response?.data || error.message);
});
import os
import json
import requests
from jose import jwe
SECRET_KEY = os.getenv('MODULUS_SECRET_KEY')
ENCRYPTION_KEY = os.getenv('MODULUS_ENCRYPTION_KEY')
def create_webhook():
# 1. Prepare the payload
payload = {
'webhookAction': 'QRPH_SUCCESS',
'callbackUrl': 'https://api.yourcompany.com/webhooks/success'
}
# 2. Encrypt payload into JWE token
jwe_token = jwe.encrypt(
json.dumps(payload),
ENCRYPTION_KEY,
algorithm='A256KW',
encryption='A256CBC-HS512'
)
# 3. Make API request
response = requests.post(
'https://webhooks.sbx.moduluslabs.io/v1/webhooks',
json={'request': {'Token': jwe_token}},
auth=(SECRET_KEY, ''),
headers={'Content-Type': 'application/json'}
)
response.raise_for_status()
result = response.json()
print('Webhook created successfully')
print(f"Webhook ID: {result['id']}")
print(f"Webhook URL: {result['callbackUrl']}")
print(f"Status: {result['webhookStatus']}")
return result
if __name__ == '__main__':
try:
create_webhook()
except Exception as e:
print(f'Error: {str(e)}')
<?php
require 'vendor/autoload.php';
use Jose\Component\Core\AlgorithmManager;
use Jose\Component\Core\JWK;
use Jose\Component\Encryption\Algorithm\KeyEncryption\A256KW;
use Jose\Component\Encryption\Algorithm\ContentEncryption\A256CBCHS512;
use Jose\Component\Encryption\JWEBuilder;
use Jose\Component\Encryption\Serializer\CompactSerializer;
$secretKey = getenv('MODULUS_SECRET_KEY');
$encryptionKey = getenv('MODULUS_ENCRYPTION_KEY');
function createWebhook() {
global $secretKey, $encryptionKey;
// 1. Prepare the payload
$payload = [
'webhookAction' => 'QRPH_SUCCESS',
'callbackUrl' => 'https://api.yourcompany.com/webhooks/success'
];
// 2. Encrypt payload (simplified - use proper JWE library)
// See encryption documentation for full implementation
$jweToken = encryptToJWE(json_encode($payload), $encryptionKey);
// 3. Make API request
$ch = curl_init('https://webhooks.sbx.moduluslabs.io/v1/webhooks');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['request' => ['Token' => $jweToken]]));
curl_setopt($ch, CURLOPT_USERPWD, $secretKey . ':');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 201) {
$result = json_decode($response, true);
echo "Webhook created successfully\n";
echo "Webhook ID: {$result['id']}\n";
echo "Webhook URL: {$result['callbackUrl']}\n";
echo "Status: {$result['webhookStatus']}\n";
return $result;
} else {
echo "Error: HTTP $httpCode\n";
echo "Details: $response\n";
return null;
}
}
try {
createWebhook();
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
}
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.*;
import com.nimbusds.jose.jwk.OctetSequenceKey;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URI;
import java.net.http.*;
import java.util.*;
public class CreateWebhookExample {
private static final ObjectMapper mapper = new ObjectMapper();
public static void main(String[] args) throws Exception {
createWebhook();
}
static void createWebhook() throws Exception {
String secretKey = System.getenv("MODULUS_SECRET_KEY");
String encryptionKey = System.getenv("MODULUS_ENCRYPTION_KEY");
// 1. Prepare the payload
Map<String, Object> payload = new LinkedHashMap<>();
payload.put("webhookAction", "QRPH_SUCCESS");
payload.put("callbackUrl", "https://api.yourcompany.com/webhooks/success");
String payloadJson = mapper.writeValueAsString(payload);
// 2. Encrypt payload into JWE token
byte[] keyBytes = Base64.getUrlDecoder().decode(encryptionKey);
OctetSequenceKey jwk = new OctetSequenceKey.Builder(keyBytes).build();
JWEHeader header = new JWEHeader(JWEAlgorithm.A256KW, EncryptionMethod.A256CBC_HS512);
JWEObject jwe = new JWEObject(header, new Payload(payloadJson));
jwe.encrypt(new AESEncrypter(jwk));
String jweToken = jwe.serialize();
// 3. Make API request with Basic Auth
HttpClient client = HttpClient.newHttpClient();
String auth = secretKey + ":";
String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes());
String requestBody = mapper.writeValueAsString(Map.of("request", Map.of("Token", jweToken)));
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://webhooks.sbx.moduluslabs.io/v1/webhooks"))
.header("Authorization", "Basic " + encodedAuth)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 201) {
Map<String, Object> result = mapper.readValue(response.body(), Map.class);
System.out.println("Webhook created successfully");
System.out.println("Webhook ID: " + result.get("id"));
System.out.println("Webhook URL: " + result.get("callbackUrl"));
System.out.println("Status: " + result.get("webhookStatus"));
} else {
System.out.println("Error: HTTP " + response.statusCode());
System.out.println("Details: " + response.body());
}
}
}
package main
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"github.com/lestrrat-go/jwx/v2/jwa"
"github.com/lestrrat-go/jwx/v2/jwe"
)
type WebhookPayload struct {
WebhookAction string `json:"webhookAction"`
CallbackUrl string `json:"callbackUrl"`
}
type TokenWrapper struct {
Token string `json:"Token"`
}
type TokenRequest struct {
Request TokenWrapper `json:"request"`
}
type WebhookResponse struct {
ID string `json:"id"`
WebhookAction string `json:"webhookAction"`
WebhookStatus string `json:"webhookStatus"`
CallbackUrl string `json:"callbackUrl"`
}
func main() {
if err := createWebhook(); err != nil {
fmt.Printf("Error: %v\n", err)
}
}
func createWebhook() error {
secretKey := os.Getenv("MODULUS_SECRET_KEY")
encryptionKey := os.Getenv("MODULUS_ENCRYPTION_KEY")
// 1. Prepare the payload
payload := WebhookPayload{
WebhookAction: "QRPH_SUCCESS",
CallbackUrl: "https://api.yourcompany.com/webhooks/success",
}
payloadJSON, err := json.Marshal(payload)
if err != nil {
return fmt.Errorf("failed to marshal payload: %w", err)
}
// 2. Encrypt payload into JWE token
keyBytes, err := base64.RawURLEncoding.DecodeString(encryptionKey)
if err != nil {
return fmt.Errorf("failed to decode encryption key: %w", err)
}
encrypted, err := jwe.Encrypt(payloadJSON,
jwe.WithKey(jwa.A256KW, keyBytes),
jwe.WithContentEncryption(jwa.A256CBC_HS512),
)
if err != nil {
return fmt.Errorf("failed to encrypt payload: %w", err)
}
jweToken := string(encrypted)
// 3. Make API request with Basic Auth
reqBody := TokenRequest{Request: TokenWrapper{Token: jweToken}}
jsonData, _ := json.Marshal(reqBody)
req, err := http.NewRequest("POST", "https://webhooks.sbx.moduluslabs.io/v1/webhooks", bytes.NewBuffer(jsonData))
if err != nil {
return fmt.Errorf("failed to create request: %w", err)
}
req.SetBasicAuth(secretKey, "")
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
if resp.StatusCode == 201 {
var result WebhookResponse
json.Unmarshal(body, &result)
fmt.Println("Webhook created successfully")
fmt.Printf("Webhook ID: %s\n", result.ID)
fmt.Printf("Webhook URL: %s\n", result.CallbackUrl)
fmt.Printf("Status: %s\n", result.WebhookStatus)
return nil
}
return fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(body))
}
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using Jose;
namespace ModulusWebhooks;
class Program
{
static async Task Main(string[] args)
{
await CreateWebhook();
}
static byte[] Base64UrlDecode(string base64Url)
{
var base64 = base64Url
.Replace('-', '+')
.Replace('_', '/');
switch (base64.Length % 4)
{
case 2: base64 += "=="; break;
case 3: base64 += "="; break;
}
return Convert.FromBase64String(base64);
}
static async Task CreateWebhook()
{
var secretKey = Environment.GetEnvironmentVariable("MODULUS_SECRET_KEY");
var encryptionKey = Environment.GetEnvironmentVariable("MODULUS_ENCRYPTION_KEY");
if (string.IsNullOrEmpty(secretKey) || string.IsNullOrEmpty(encryptionKey))
{
Console.WriteLine("Error: MODULUS_SECRET_KEY and MODULUS_ENCRYPTION_KEY must be set");
return;
}
try
{
// 1. Prepare the payload
var payload = new
{
webhookAction = "QRPH_SUCCESS",
callbackUrl = "https://api.yourcompany.com/webhooks/success"
};
// 2. Encrypt payload into JWE token
var key = Base64UrlDecode(encryptionKey);
var payloadJson = JsonSerializer.Serialize(payload);
var jweToken = JWT.Encode(
payloadJson,
key,
JweAlgorithm.A256KW,
JweEncryption.A256CBC_HS512
);
// 3. Make API request with Basic Auth
using var client = new HttpClient();
var authBytes = Encoding.ASCII.GetBytes($"{secretKey}:");
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Basic", Convert.ToBase64String(authBytes));
var requestBody = new { request = new { Token = jweToken } };
var content = new StringContent(
JsonSerializer.Serialize(requestBody),
Encoding.UTF8,
"application/json"
);
var response = await client.PostAsync(
"https://webhooks.sbx.moduluslabs.io/v1/webhooks",
content
);
var responseBody = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
var result = JsonSerializer.Deserialize<WebhookResponse>(
responseBody,
new JsonSerializerOptions { PropertyNameCaseInsensitive = true }
);
Console.WriteLine("Webhook created successfully");
Console.WriteLine($"Webhook ID: {result?.Id}");
Console.WriteLine($"Webhook URL: {result?.CallbackUrl}");
Console.WriteLine($"Action: {result?.WebhookAction}");
Console.WriteLine($"Status: {result?.WebhookStatus}");
}
else
{
Console.WriteLine($"Error: HTTP {(int)response.StatusCode}");
Console.WriteLine($"Details: {responseBody}");
}
}
catch (Exception e)
{
Console.WriteLine($"Error: {e.Message}");
}
}
}
public class WebhookResponse
{
public string? Id { get; set; }
public string? CallbackUrl { get; set; }
public string? WebhookAction { get; set; }
public string? WebhookStatus { get; set; }
}
{
"id": "a78efd32-de3b-4854-b599-11ae9f98f97e",
"webhookAction": "QRPH_SUCCESS",
"webhookStatus": "ENABLED",
"callbackUrl": "https://api.yourcompany.com/webhooks/success"
}
{
"code": "20000002",
"error": "Invalid callbackUrl: must be a valid HTTPS URL",
"referenceNumber": "097bf60a-bdab-40bf-b615-3c0eae693b86"
}
{
"code": "10000013",
"error": "Invalid API Key",
"referenceNumber": "097bf60a-bdab-40bf-b615-3c0eae693b86"
}
{
"code": "20000003",
"error": "Webhook URL already exists",
"referenceNumber": "097bf60a-bdab-40bf-b615-3c0eae693b86"
}
{
"code": "10000001",
"error": "An unexpected error occurred. Please try again later.",
"referenceNumber": "097bf60a-bdab-40bf-b615-3c0eae693b86"
}
Create Webhook
Register a new webhook endpoint to receive QR Ph transaction notifications
# Note: You need to encrypt the payload first using a script/tool
# See the Encryption Guide for JWE token creation
# 1. Create encrypted JWE token (using external tool/script)
ENCRYPTED_TOKEN="eyJhbGciOiJBMjU2S1ciL..."
# 2. Make API request
curl -X POST https://webhooks.sbx.moduluslabs.io/v1/webhooks \
-u sk_your_secret_key: \
-H "Content-Type: application/json" \
-d "{\"request\": {\"Token\": \"$ENCRYPTED_TOKEN\"}}"
# 3. Response contains the created webhook object
const axios = require('axios');
const jose = require('jose');
const SECRET_KEY = process.env.MODULUS_SECRET_KEY;
const ENCRYPTION_KEY = process.env.MODULUS_ENCRYPTION_KEY;
async function createWebhook() {
// 1. Prepare the payload
const payload = {
webhookAction: 'QRPH_SUCCESS',
callbackUrl: 'https://api.yourcompany.com/webhooks/success'
};
// 2. Encrypt payload into JWE token
const key = Buffer.from(ENCRYPTION_KEY, 'base64');
const jweToken = await new jose.CompactEncrypt(
new TextEncoder().encode(JSON.stringify(payload))
)
.setProtectedHeader({ alg: 'A256KW', enc: 'A256CBC-HS512' })
.encrypt(key);
// 3. Make API request
const response = await axios.post(
'https://webhooks.sbx.moduluslabs.io/v1/webhooks',
{ request: { Token: jweToken } },
{
auth: { username: SECRET_KEY, password: '' },
headers: { 'Content-Type': 'application/json' }
}
);
const result = response.data;
console.log('Webhook created successfully');
console.log('Webhook ID:', result.id);
console.log('Webhook URL:', result.callbackUrl);
console.log('Action:', result.webhookAction);
console.log('Status:', result.webhookStatus);
return result;
}
createWebhook()
.catch(error => {
console.error('Error:', error.response?.data || error.message);
});
import os
import json
import requests
from jose import jwe
SECRET_KEY = os.getenv('MODULUS_SECRET_KEY')
ENCRYPTION_KEY = os.getenv('MODULUS_ENCRYPTION_KEY')
def create_webhook():
# 1. Prepare the payload
payload = {
'webhookAction': 'QRPH_SUCCESS',
'callbackUrl': 'https://api.yourcompany.com/webhooks/success'
}
# 2. Encrypt payload into JWE token
jwe_token = jwe.encrypt(
json.dumps(payload),
ENCRYPTION_KEY,
algorithm='A256KW',
encryption='A256CBC-HS512'
)
# 3. Make API request
response = requests.post(
'https://webhooks.sbx.moduluslabs.io/v1/webhooks',
json={'request': {'Token': jwe_token}},
auth=(SECRET_KEY, ''),
headers={'Content-Type': 'application/json'}
)
response.raise_for_status()
result = response.json()
print('Webhook created successfully')
print(f"Webhook ID: {result['id']}")
print(f"Webhook URL: {result['callbackUrl']}")
print(f"Status: {result['webhookStatus']}")
return result
if __name__ == '__main__':
try:
create_webhook()
except Exception as e:
print(f'Error: {str(e)}')
<?php
require 'vendor/autoload.php';
use Jose\Component\Core\AlgorithmManager;
use Jose\Component\Core\JWK;
use Jose\Component\Encryption\Algorithm\KeyEncryption\A256KW;
use Jose\Component\Encryption\Algorithm\ContentEncryption\A256CBCHS512;
use Jose\Component\Encryption\JWEBuilder;
use Jose\Component\Encryption\Serializer\CompactSerializer;
$secretKey = getenv('MODULUS_SECRET_KEY');
$encryptionKey = getenv('MODULUS_ENCRYPTION_KEY');
function createWebhook() {
global $secretKey, $encryptionKey;
// 1. Prepare the payload
$payload = [
'webhookAction' => 'QRPH_SUCCESS',
'callbackUrl' => 'https://api.yourcompany.com/webhooks/success'
];
// 2. Encrypt payload (simplified - use proper JWE library)
// See encryption documentation for full implementation
$jweToken = encryptToJWE(json_encode($payload), $encryptionKey);
// 3. Make API request
$ch = curl_init('https://webhooks.sbx.moduluslabs.io/v1/webhooks');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['request' => ['Token' => $jweToken]]));
curl_setopt($ch, CURLOPT_USERPWD, $secretKey . ':');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 201) {
$result = json_decode($response, true);
echo "Webhook created successfully\n";
echo "Webhook ID: {$result['id']}\n";
echo "Webhook URL: {$result['callbackUrl']}\n";
echo "Status: {$result['webhookStatus']}\n";
return $result;
} else {
echo "Error: HTTP $httpCode\n";
echo "Details: $response\n";
return null;
}
}
try {
createWebhook();
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
}
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.*;
import com.nimbusds.jose.jwk.OctetSequenceKey;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URI;
import java.net.http.*;
import java.util.*;
public class CreateWebhookExample {
private static final ObjectMapper mapper = new ObjectMapper();
public static void main(String[] args) throws Exception {
createWebhook();
}
static void createWebhook() throws Exception {
String secretKey = System.getenv("MODULUS_SECRET_KEY");
String encryptionKey = System.getenv("MODULUS_ENCRYPTION_KEY");
// 1. Prepare the payload
Map<String, Object> payload = new LinkedHashMap<>();
payload.put("webhookAction", "QRPH_SUCCESS");
payload.put("callbackUrl", "https://api.yourcompany.com/webhooks/success");
String payloadJson = mapper.writeValueAsString(payload);
// 2. Encrypt payload into JWE token
byte[] keyBytes = Base64.getUrlDecoder().decode(encryptionKey);
OctetSequenceKey jwk = new OctetSequenceKey.Builder(keyBytes).build();
JWEHeader header = new JWEHeader(JWEAlgorithm.A256KW, EncryptionMethod.A256CBC_HS512);
JWEObject jwe = new JWEObject(header, new Payload(payloadJson));
jwe.encrypt(new AESEncrypter(jwk));
String jweToken = jwe.serialize();
// 3. Make API request with Basic Auth
HttpClient client = HttpClient.newHttpClient();
String auth = secretKey + ":";
String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes());
String requestBody = mapper.writeValueAsString(Map.of("request", Map.of("Token", jweToken)));
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://webhooks.sbx.moduluslabs.io/v1/webhooks"))
.header("Authorization", "Basic " + encodedAuth)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 201) {
Map<String, Object> result = mapper.readValue(response.body(), Map.class);
System.out.println("Webhook created successfully");
System.out.println("Webhook ID: " + result.get("id"));
System.out.println("Webhook URL: " + result.get("callbackUrl"));
System.out.println("Status: " + result.get("webhookStatus"));
} else {
System.out.println("Error: HTTP " + response.statusCode());
System.out.println("Details: " + response.body());
}
}
}
package main
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"github.com/lestrrat-go/jwx/v2/jwa"
"github.com/lestrrat-go/jwx/v2/jwe"
)
type WebhookPayload struct {
WebhookAction string `json:"webhookAction"`
CallbackUrl string `json:"callbackUrl"`
}
type TokenWrapper struct {
Token string `json:"Token"`
}
type TokenRequest struct {
Request TokenWrapper `json:"request"`
}
type WebhookResponse struct {
ID string `json:"id"`
WebhookAction string `json:"webhookAction"`
WebhookStatus string `json:"webhookStatus"`
CallbackUrl string `json:"callbackUrl"`
}
func main() {
if err := createWebhook(); err != nil {
fmt.Printf("Error: %v\n", err)
}
}
func createWebhook() error {
secretKey := os.Getenv("MODULUS_SECRET_KEY")
encryptionKey := os.Getenv("MODULUS_ENCRYPTION_KEY")
// 1. Prepare the payload
payload := WebhookPayload{
WebhookAction: "QRPH_SUCCESS",
CallbackUrl: "https://api.yourcompany.com/webhooks/success",
}
payloadJSON, err := json.Marshal(payload)
if err != nil {
return fmt.Errorf("failed to marshal payload: %w", err)
}
// 2. Encrypt payload into JWE token
keyBytes, err := base64.RawURLEncoding.DecodeString(encryptionKey)
if err != nil {
return fmt.Errorf("failed to decode encryption key: %w", err)
}
encrypted, err := jwe.Encrypt(payloadJSON,
jwe.WithKey(jwa.A256KW, keyBytes),
jwe.WithContentEncryption(jwa.A256CBC_HS512),
)
if err != nil {
return fmt.Errorf("failed to encrypt payload: %w", err)
}
jweToken := string(encrypted)
// 3. Make API request with Basic Auth
reqBody := TokenRequest{Request: TokenWrapper{Token: jweToken}}
jsonData, _ := json.Marshal(reqBody)
req, err := http.NewRequest("POST", "https://webhooks.sbx.moduluslabs.io/v1/webhooks", bytes.NewBuffer(jsonData))
if err != nil {
return fmt.Errorf("failed to create request: %w", err)
}
req.SetBasicAuth(secretKey, "")
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
if resp.StatusCode == 201 {
var result WebhookResponse
json.Unmarshal(body, &result)
fmt.Println("Webhook created successfully")
fmt.Printf("Webhook ID: %s\n", result.ID)
fmt.Printf("Webhook URL: %s\n", result.CallbackUrl)
fmt.Printf("Status: %s\n", result.WebhookStatus)
return nil
}
return fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(body))
}
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using Jose;
namespace ModulusWebhooks;
class Program
{
static async Task Main(string[] args)
{
await CreateWebhook();
}
static byte[] Base64UrlDecode(string base64Url)
{
var base64 = base64Url
.Replace('-', '+')
.Replace('_', '/');
switch (base64.Length % 4)
{
case 2: base64 += "=="; break;
case 3: base64 += "="; break;
}
return Convert.FromBase64String(base64);
}
static async Task CreateWebhook()
{
var secretKey = Environment.GetEnvironmentVariable("MODULUS_SECRET_KEY");
var encryptionKey = Environment.GetEnvironmentVariable("MODULUS_ENCRYPTION_KEY");
if (string.IsNullOrEmpty(secretKey) || string.IsNullOrEmpty(encryptionKey))
{
Console.WriteLine("Error: MODULUS_SECRET_KEY and MODULUS_ENCRYPTION_KEY must be set");
return;
}
try
{
// 1. Prepare the payload
var payload = new
{
webhookAction = "QRPH_SUCCESS",
callbackUrl = "https://api.yourcompany.com/webhooks/success"
};
// 2. Encrypt payload into JWE token
var key = Base64UrlDecode(encryptionKey);
var payloadJson = JsonSerializer.Serialize(payload);
var jweToken = JWT.Encode(
payloadJson,
key,
JweAlgorithm.A256KW,
JweEncryption.A256CBC_HS512
);
// 3. Make API request with Basic Auth
using var client = new HttpClient();
var authBytes = Encoding.ASCII.GetBytes($"{secretKey}:");
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Basic", Convert.ToBase64String(authBytes));
var requestBody = new { request = new { Token = jweToken } };
var content = new StringContent(
JsonSerializer.Serialize(requestBody),
Encoding.UTF8,
"application/json"
);
var response = await client.PostAsync(
"https://webhooks.sbx.moduluslabs.io/v1/webhooks",
content
);
var responseBody = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
var result = JsonSerializer.Deserialize<WebhookResponse>(
responseBody,
new JsonSerializerOptions { PropertyNameCaseInsensitive = true }
);
Console.WriteLine("Webhook created successfully");
Console.WriteLine($"Webhook ID: {result?.Id}");
Console.WriteLine($"Webhook URL: {result?.CallbackUrl}");
Console.WriteLine($"Action: {result?.WebhookAction}");
Console.WriteLine($"Status: {result?.WebhookStatus}");
}
else
{
Console.WriteLine($"Error: HTTP {(int)response.StatusCode}");
Console.WriteLine($"Details: {responseBody}");
}
}
catch (Exception e)
{
Console.WriteLine($"Error: {e.Message}");
}
}
}
public class WebhookResponse
{
public string? Id { get; set; }
public string? CallbackUrl { get; set; }
public string? WebhookAction { get; set; }
public string? WebhookStatus { get; set; }
}
{
"id": "a78efd32-de3b-4854-b599-11ae9f98f97e",
"webhookAction": "QRPH_SUCCESS",
"webhookStatus": "ENABLED",
"callbackUrl": "https://api.yourcompany.com/webhooks/success"
}
{
"code": "20000002",
"error": "Invalid callbackUrl: must be a valid HTTPS URL",
"referenceNumber": "097bf60a-bdab-40bf-b615-3c0eae693b86"
}
{
"code": "10000013",
"error": "Invalid API Key",
"referenceNumber": "097bf60a-bdab-40bf-b615-3c0eae693b86"
}
{
"code": "20000003",
"error": "Webhook URL already exists",
"referenceNumber": "097bf60a-bdab-40bf-b615-3c0eae693b86"
}
{
"code": "10000001",
"error": "An unexpected error occurred. Please try again later.",
"referenceNumber": "097bf60a-bdab-40bf-b615-3c0eae693b86"
}
Overview
The Create Webhook endpoint allows you to register a new webhook URL that will receive real-time notifications when QR Ph transactions complete. You can specify which transaction events (success or declined) to receive and enable or disable the webhook immediately.Endpoint
POST https://webhooks.sbx.moduluslabs.io/v1/webhooks
Authentication
This endpoint requires HTTP Basic Authentication using your Secret Key.Authorization: Basic {base64(secret_key:)}
Request
Headers
| Header | Value | Required | Description |
|---|---|---|---|
Authorization | Basic {base64(secret_key:)} | Yes | HTTP Basic Auth with your secret key |
Content-Type | application/json | Yes | Request body format |
activation-code | string | No | Merchant activation code for multi-merchant setups |
"MERCHANT_ABC_123"When you create a webhook with an activation code, Modulus Labs includes the same activation-code in the header of webhook notifications sent to your endpoint.Prerequisites
Before creating a webhook, ensure you have:- Your Secret Key for HTTP Basic Authentication
Encrypted Payload Structure
The request body must contain arequest object with a JWE-encrypted Token that includes your webhook configuration:
{
"request": {
"Token": "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIn0..."
}
}
Body Parameters (Encrypted Payload)
Token field. See the Encryption Guide for details on creating JWE tokens.| Field | Type | Required | Description |
|---|---|---|---|
callbackUrl | string | Yes | HTTPS URL where Modulus Labs sends webhook notifications |
webhookAction | string | Yes | Transaction events to receive: QRPH_SUCCESS, QRPH_DECLINED |
callbackUrl
callbackUrl
- Format: Valid HTTPS URL
- Example:
"https://api.yourcompany.com/webhooks/modulus"
- Must use HTTPS protocol (HTTP not supported in production)
- Must be publicly accessible from the internet
- Must respond within 10 seconds
- Should return
200 OKto acknowledge receipt
webhookAction
webhookAction
QRPH_SUCCESS- Receive notifications when payments succeedQRPH_DECLINED- Receive notifications when payments are declined
"QRPH_SUCCESS"Example Payload (Before Encryption)
{
"webhookAction": "QRPH_SUCCESS",
"callbackUrl": "https://api.yourcompany.com/webhooks/success"
}
Response
Success Response
Status Code:201 Created
Returns the created webhook object with a unique id that you can use to update or delete the webhook later.
a78efd32-de3b-4854-b599-11ae9f98f97e"QRPH_SUCCESS" "QRPH_DECLINED"ENABLED or DISABLEDExample: "ENABLED""https://api.yourcompany.com/webhooks/modulus"Response Example
{
"id": "a78efd32-de3b-4854-b599-11ae9f98f97e",
"webhookAction": "QRPH_SUCCESS",
"webhookStatus": "ENABLED",
"callbackUrl": "https://api.yourcompany.com/webhooks/success"
}
{
"id": "a78efd32-de3b-4854-b599-11ae9f98f97e",
"webhookAction": "QRPH_SUCCESS",
"webhookStatus": "ENABLED",
"callbackUrl": "https://api.yourcompany.com/webhooks/success"
}
{
"code": "20000002",
"error": "Invalid callbackUrl: must be a valid HTTPS URL",
"referenceNumber": "097bf60a-bdab-40bf-b615-3c0eae693b86"
}
{
"code": "10000013",
"error": "Invalid API Key",
"referenceNumber": "097bf60a-bdab-40bf-b615-3c0eae693b86"
}
{
"code": "20000003",
"error": "Webhook URL already exists",
"referenceNumber": "097bf60a-bdab-40bf-b615-3c0eae693b86"
}
{
"code": "10000001",
"error": "An unexpected error occurred. Please try again later.",
"referenceNumber": "097bf60a-bdab-40bf-b615-3c0eae693b86"
}
Error Responses
400 Bad Request
400 Bad Request
400Causes:- Invalid webhook URL format
- Missing required fields
- Invalid action values
- Invalid status value
{
"code": "20000002",
"error": "Invalid callbackUrl: must be a valid HTTPS URL",
"referenceNumber": "abc123-def456-ghi789"
}
- Ensure callbackUrl uses HTTPS protocol
- Verify all required fields are present
- Check that actions contain valid values (
QRPH_SUCCESS,QRPH_DECLINED) - Ensure status is either
ENABLEDorDISABLED
401 Unauthorized
401 Unauthorized
401Cause: Invalid or missing authentication credentialsSolution:- Verify your secret key is correct
- Ensure Authorization header format:
Basic {base64(secret_key:)} - Check you’re using the right environment (sandbox vs production)
409 Conflict
409 Conflict
409Cause: Webhook URL already registered for this merchantResponse Example:{
"code": "20000003",
"error": "Webhook URL already exists",
"referenceNumber": "abc123-def456-ghi789"
}
- Use a different webhook URL, or
- Update the existing webhook using Update Webhook API, or
- Delete the existing webhook and create a new one
500 Internal Server Error
500 Internal Server Error
500Cause: Unexpected server errorSolution:- Retry the request
- If the issue persists, contact Modulus Labs support with the reference number
# Note: You need to encrypt the payload first using a script/tool
# See the Encryption Guide for JWE token creation
# 1. Create encrypted JWE token (using external tool/script)
ENCRYPTED_TOKEN="eyJhbGciOiJBMjU2S1ciL..."
# 2. Make API request
curl -X POST https://webhooks.sbx.moduluslabs.io/v1/webhooks \
-u sk_your_secret_key: \
-H "Content-Type: application/json" \
-d "{\"request\": {\"Token\": \"$ENCRYPTED_TOKEN\"}}"
# 3. Response contains the created webhook object
const axios = require('axios');
const jose = require('jose');
const SECRET_KEY = process.env.MODULUS_SECRET_KEY;
const ENCRYPTION_KEY = process.env.MODULUS_ENCRYPTION_KEY;
async function createWebhook() {
// 1. Prepare the payload
const payload = {
webhookAction: 'QRPH_SUCCESS',
callbackUrl: 'https://api.yourcompany.com/webhooks/success'
};
// 2. Encrypt payload into JWE token
const key = Buffer.from(ENCRYPTION_KEY, 'base64');
const jweToken = await new jose.CompactEncrypt(
new TextEncoder().encode(JSON.stringify(payload))
)
.setProtectedHeader({ alg: 'A256KW', enc: 'A256CBC-HS512' })
.encrypt(key);
// 3. Make API request
const response = await axios.post(
'https://webhooks.sbx.moduluslabs.io/v1/webhooks',
{ request: { Token: jweToken } },
{
auth: { username: SECRET_KEY, password: '' },
headers: { 'Content-Type': 'application/json' }
}
);
const result = response.data;
console.log('Webhook created successfully');
console.log('Webhook ID:', result.id);
console.log('Webhook URL:', result.callbackUrl);
console.log('Action:', result.webhookAction);
console.log('Status:', result.webhookStatus);
return result;
}
createWebhook()
.catch(error => {
console.error('Error:', error.response?.data || error.message);
});
import os
import json
import requests
from jose import jwe
SECRET_KEY = os.getenv('MODULUS_SECRET_KEY')
ENCRYPTION_KEY = os.getenv('MODULUS_ENCRYPTION_KEY')
def create_webhook():
# 1. Prepare the payload
payload = {
'webhookAction': 'QRPH_SUCCESS',
'callbackUrl': 'https://api.yourcompany.com/webhooks/success'
}
# 2. Encrypt payload into JWE token
jwe_token = jwe.encrypt(
json.dumps(payload),
ENCRYPTION_KEY,
algorithm='A256KW',
encryption='A256CBC-HS512'
)
# 3. Make API request
response = requests.post(
'https://webhooks.sbx.moduluslabs.io/v1/webhooks',
json={'request': {'Token': jwe_token}},
auth=(SECRET_KEY, ''),
headers={'Content-Type': 'application/json'}
)
response.raise_for_status()
result = response.json()
print('Webhook created successfully')
print(f"Webhook ID: {result['id']}")
print(f"Webhook URL: {result['callbackUrl']}")
print(f"Status: {result['webhookStatus']}")
return result
if __name__ == '__main__':
try:
create_webhook()
except Exception as e:
print(f'Error: {str(e)}')
<?php
require 'vendor/autoload.php';
use Jose\Component\Core\AlgorithmManager;
use Jose\Component\Core\JWK;
use Jose\Component\Encryption\Algorithm\KeyEncryption\A256KW;
use Jose\Component\Encryption\Algorithm\ContentEncryption\A256CBCHS512;
use Jose\Component\Encryption\JWEBuilder;
use Jose\Component\Encryption\Serializer\CompactSerializer;
$secretKey = getenv('MODULUS_SECRET_KEY');
$encryptionKey = getenv('MODULUS_ENCRYPTION_KEY');
function createWebhook() {
global $secretKey, $encryptionKey;
// 1. Prepare the payload
$payload = [
'webhookAction' => 'QRPH_SUCCESS',
'callbackUrl' => 'https://api.yourcompany.com/webhooks/success'
];
// 2. Encrypt payload (simplified - use proper JWE library)
// See encryption documentation for full implementation
$jweToken = encryptToJWE(json_encode($payload), $encryptionKey);
// 3. Make API request
$ch = curl_init('https://webhooks.sbx.moduluslabs.io/v1/webhooks');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['request' => ['Token' => $jweToken]]));
curl_setopt($ch, CURLOPT_USERPWD, $secretKey . ':');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 201) {
$result = json_decode($response, true);
echo "Webhook created successfully\n";
echo "Webhook ID: {$result['id']}\n";
echo "Webhook URL: {$result['callbackUrl']}\n";
echo "Status: {$result['webhookStatus']}\n";
return $result;
} else {
echo "Error: HTTP $httpCode\n";
echo "Details: $response\n";
return null;
}
}
try {
createWebhook();
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
}
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.*;
import com.nimbusds.jose.jwk.OctetSequenceKey;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URI;
import java.net.http.*;
import java.util.*;
public class CreateWebhookExample {
private static final ObjectMapper mapper = new ObjectMapper();
public static void main(String[] args) throws Exception {
createWebhook();
}
static void createWebhook() throws Exception {
String secretKey = System.getenv("MODULUS_SECRET_KEY");
String encryptionKey = System.getenv("MODULUS_ENCRYPTION_KEY");
// 1. Prepare the payload
Map<String, Object> payload = new LinkedHashMap<>();
payload.put("webhookAction", "QRPH_SUCCESS");
payload.put("callbackUrl", "https://api.yourcompany.com/webhooks/success");
String payloadJson = mapper.writeValueAsString(payload);
// 2. Encrypt payload into JWE token
byte[] keyBytes = Base64.getUrlDecoder().decode(encryptionKey);
OctetSequenceKey jwk = new OctetSequenceKey.Builder(keyBytes).build();
JWEHeader header = new JWEHeader(JWEAlgorithm.A256KW, EncryptionMethod.A256CBC_HS512);
JWEObject jwe = new JWEObject(header, new Payload(payloadJson));
jwe.encrypt(new AESEncrypter(jwk));
String jweToken = jwe.serialize();
// 3. Make API request with Basic Auth
HttpClient client = HttpClient.newHttpClient();
String auth = secretKey + ":";
String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes());
String requestBody = mapper.writeValueAsString(Map.of("request", Map.of("Token", jweToken)));
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://webhooks.sbx.moduluslabs.io/v1/webhooks"))
.header("Authorization", "Basic " + encodedAuth)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 201) {
Map<String, Object> result = mapper.readValue(response.body(), Map.class);
System.out.println("Webhook created successfully");
System.out.println("Webhook ID: " + result.get("id"));
System.out.println("Webhook URL: " + result.get("callbackUrl"));
System.out.println("Status: " + result.get("webhookStatus"));
} else {
System.out.println("Error: HTTP " + response.statusCode());
System.out.println("Details: " + response.body());
}
}
}
package main
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"github.com/lestrrat-go/jwx/v2/jwa"
"github.com/lestrrat-go/jwx/v2/jwe"
)
type WebhookPayload struct {
WebhookAction string `json:"webhookAction"`
CallbackUrl string `json:"callbackUrl"`
}
type TokenWrapper struct {
Token string `json:"Token"`
}
type TokenRequest struct {
Request TokenWrapper `json:"request"`
}
type WebhookResponse struct {
ID string `json:"id"`
WebhookAction string `json:"webhookAction"`
WebhookStatus string `json:"webhookStatus"`
CallbackUrl string `json:"callbackUrl"`
}
func main() {
if err := createWebhook(); err != nil {
fmt.Printf("Error: %v\n", err)
}
}
func createWebhook() error {
secretKey := os.Getenv("MODULUS_SECRET_KEY")
encryptionKey := os.Getenv("MODULUS_ENCRYPTION_KEY")
// 1. Prepare the payload
payload := WebhookPayload{
WebhookAction: "QRPH_SUCCESS",
CallbackUrl: "https://api.yourcompany.com/webhooks/success",
}
payloadJSON, err := json.Marshal(payload)
if err != nil {
return fmt.Errorf("failed to marshal payload: %w", err)
}
// 2. Encrypt payload into JWE token
keyBytes, err := base64.RawURLEncoding.DecodeString(encryptionKey)
if err != nil {
return fmt.Errorf("failed to decode encryption key: %w", err)
}
encrypted, err := jwe.Encrypt(payloadJSON,
jwe.WithKey(jwa.A256KW, keyBytes),
jwe.WithContentEncryption(jwa.A256CBC_HS512),
)
if err != nil {
return fmt.Errorf("failed to encrypt payload: %w", err)
}
jweToken := string(encrypted)
// 3. Make API request with Basic Auth
reqBody := TokenRequest{Request: TokenWrapper{Token: jweToken}}
jsonData, _ := json.Marshal(reqBody)
req, err := http.NewRequest("POST", "https://webhooks.sbx.moduluslabs.io/v1/webhooks", bytes.NewBuffer(jsonData))
if err != nil {
return fmt.Errorf("failed to create request: %w", err)
}
req.SetBasicAuth(secretKey, "")
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
if resp.StatusCode == 201 {
var result WebhookResponse
json.Unmarshal(body, &result)
fmt.Println("Webhook created successfully")
fmt.Printf("Webhook ID: %s\n", result.ID)
fmt.Printf("Webhook URL: %s\n", result.CallbackUrl)
fmt.Printf("Status: %s\n", result.WebhookStatus)
return nil
}
return fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(body))
}
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using Jose;
namespace ModulusWebhooks;
class Program
{
static async Task Main(string[] args)
{
await CreateWebhook();
}
static byte[] Base64UrlDecode(string base64Url)
{
var base64 = base64Url
.Replace('-', '+')
.Replace('_', '/');
switch (base64.Length % 4)
{
case 2: base64 += "=="; break;
case 3: base64 += "="; break;
}
return Convert.FromBase64String(base64);
}
static async Task CreateWebhook()
{
var secretKey = Environment.GetEnvironmentVariable("MODULUS_SECRET_KEY");
var encryptionKey = Environment.GetEnvironmentVariable("MODULUS_ENCRYPTION_KEY");
if (string.IsNullOrEmpty(secretKey) || string.IsNullOrEmpty(encryptionKey))
{
Console.WriteLine("Error: MODULUS_SECRET_KEY and MODULUS_ENCRYPTION_KEY must be set");
return;
}
try
{
// 1. Prepare the payload
var payload = new
{
webhookAction = "QRPH_SUCCESS",
callbackUrl = "https://api.yourcompany.com/webhooks/success"
};
// 2. Encrypt payload into JWE token
var key = Base64UrlDecode(encryptionKey);
var payloadJson = JsonSerializer.Serialize(payload);
var jweToken = JWT.Encode(
payloadJson,
key,
JweAlgorithm.A256KW,
JweEncryption.A256CBC_HS512
);
// 3. Make API request with Basic Auth
using var client = new HttpClient();
var authBytes = Encoding.ASCII.GetBytes($"{secretKey}:");
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Basic", Convert.ToBase64String(authBytes));
var requestBody = new { request = new { Token = jweToken } };
var content = new StringContent(
JsonSerializer.Serialize(requestBody),
Encoding.UTF8,
"application/json"
);
var response = await client.PostAsync(
"https://webhooks.sbx.moduluslabs.io/v1/webhooks",
content
);
var responseBody = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
var result = JsonSerializer.Deserialize<WebhookResponse>(
responseBody,
new JsonSerializerOptions { PropertyNameCaseInsensitive = true }
);
Console.WriteLine("Webhook created successfully");
Console.WriteLine($"Webhook ID: {result?.Id}");
Console.WriteLine($"Webhook URL: {result?.CallbackUrl}");
Console.WriteLine($"Action: {result?.WebhookAction}");
Console.WriteLine($"Status: {result?.WebhookStatus}");
}
else
{
Console.WriteLine($"Error: HTTP {(int)response.StatusCode}");
Console.WriteLine($"Details: {responseBody}");
}
}
catch (Exception e)
{
Console.WriteLine($"Error: {e.Message}");
}
}
}
public class WebhookResponse
{
public string? Id { get; set; }
public string? CallbackUrl { get; set; }
public string? WebhookAction { get; set; }
public string? WebhookStatus { get; set; }
}
Multi-Merchant Setup
If you manage multiple merchants, use theactivation-code header to associate the webhook with a specific merchant:
# Encrypt your payload into a JWE token first
ENCRYPTED_TOKEN="eyJhbGciOiJBMjU2S1ciL..."
curl -X POST https://webhooks.sbx.moduluslabs.io/v1/webhooks \
-u sk_your_secret_key: \
-H "Content-Type: application/json" \
-H "activation-code: MERCHANT_ABC_123" \
-d "{\"request\": {\"Token\": \"$ENCRYPTED_TOKEN\"}}"
activation-code header will be included in the request:
app.post('/webhooks/modulus', (req, res) => {
const activationCode = req.headers['activation-code'];
// "MERCHANT_ABC_123"
// Route to appropriate merchant handler
const merchant = getMerchantByActivationCode(activationCode);
processWebhook(merchant, req.body);
});
Use Cases
E-commerce Integration
E-commerce Integration
const webhook = await createWebhook({
callbackUrl: 'https://shop.example.com/api/payments/webhook',
webhookAction: 'QRPH_SUCCESS',
status: 'ENABLED'
});
// Webhook will notify you instantly when customers complete QR Ph payments
POS System
POS System
const webhook = await createWebhook({
callbackUrl: 'https://pos.example.com/webhooks/payment',
webhookAction: ['QRPH_SUCCESS'], // Only success events for POS
status: 'ENABLED'
});
Development and Testing
Development and Testing
// Create disabled webhook for testing
const webhook = await createWebhook({
callbackUrl: 'https://dev.example.com/webhooks/test',
webhookAction: 'QRPH_SUCCESS',
status: 'DISABLED' // Won't receive notifications yet
});
// Enable later using Update Webhook API
await updateWebhook(webhook.id, { status: 'ENABLED' });
Best Practices
Use HTTPS Only
Save Webhook ID
id in your database. You’ll need it to update or delete the webhook.Test Before Enabling
DISABLED status during development. Test with the Simulate API before enabling.Include Both Actions
QRPH_SUCCESS and QRPH_DECLINED to handle all transaction outcomes.Unique URLs per Environment
Verify Connectivity First
Validation Checklist
Before creating a webhook, verify:Webhook Endpoint is Ready
- Endpoint accepts POST requests
- Endpoint responds within 10 seconds
- Endpoint can decrypt JWE payloads
- Endpoint implements idempotency
URL is Accessible
- URL uses HTTPS protocol
- URL is publicly accessible from the internet
- Firewall allows incoming requests
- SSL certificate is valid
Authentication is Configured
- Secret key is correct
- Authorization header format is valid
- Using correct environment (sandbox vs production)
Configuration is Correct
- Webhook URL is a valid HTTPS URL
- Actions include at least one valid value
- Status is either ENABLED or DISABLED
What Happens Next?
After creating a webhook:Webhook is Registered
Transactions Trigger Notifications
Automatic Retries
200 OK, Modulus Labs automatically retries up to 3 more times at 15-minute intervals.You Process Webhooks
Next Steps
Test Your Webhook
View Webhooks
Update Webhook
Receiving Webhooks
Authorizations
HTTP Basic Authentication using your Secret Key as the username and an empty password
Headers
Optional activation code to identify which merchant this webhook belongs to. Used for multi-merchant environments.
Body
Show child attributes
Show child attributes
Response
Webhook created successfully
Unique identifier for the webhook
List of transaction events this webhook receives
QRPH_SUCCESS, QRPH_DECLINED Current status of the webhook
ENABLED, DISABLED The HTTPS URL where notifications are sent
Was this page helpful?