The Quab Express API is only available for users with corporate accounts.
The Quab Express API is organized around REST. Our API has predictable resource-oriented URLs, accepts JSON-encoded request bodies, returns JSON-encoded responses, and uses standard HTTP response codes, authentication, and verbs.
You can use the Quab Express API in test mode, which doesn’t affect your live data or interact with the package delivery networks. The API key you use to authenticate the request determines whether the request is live mode or test mode.
The Quab Express API doesn’t support bulk updates. You can work on only one object per request.
The Quab Express API differs for every account as we release new versions and tailor functionality.
https://api.quabexpress.com
The Quab Express API uses API keys to authenticate requests. You can view and manage your API keys in the Quab Express Dashboard.
Test mode public keys have the prefix pk_test_
and live mode secret keys have the prefix pk_live_
.
Test mode secret keys have the prefix sk_test_
and live mode secret keys have the prefix sk_live_
. Both public and security keys need to be kept secure.
Your API keys carry many privileges, so be sure to keep them secure! Do not share your secret API keys in publicly accessible areas such as GitHub, client-side code, and so forth.
All API requests must be made over HTTPS. Calls made over plain HTTP will fail. API requests without authentication will also fail.
A sample test API key is included in all the examples here, so you can test any example right away. Do not submit any personally identifiable information in requests made with this key.
To test requests using your account, replace the sample API key with your actual API key or sign in.
Quab Express uses conventional HTTP response codes to indicate the success or failure of an API request. In general: Codes in the 2xx
range indicate success. Codes in the 4xx
range indicate an error that failed given the information provided (e.g., a required parameter was omitted, a charge failed, etc.). Codes in the 5xx
range indicate an error with Quab Express’ servers (these are rare).
Some 4xx
errors that could be handled programmatically (e.g., a card is declined) include an error code that briefly explains the error reported.
The name of the status of the request. In this case, it will be, Error
A human-readable message providing more details about the error.
A short string indicating how to proceed with an error.
200 | OK | Everything worked as expected |
400 | Bad Request | The request was unacceptable, often due to missing a required parameter. |
401 | Unauthorized | No valid API key provided. |
402 | Request Failed | The parameters were valid but the request failed. |
403 | Forbidden | The API key doesn't have permissions to perform the request. |
404 | Not Found | The requested resource doesn't exist. |
409 | Conflict | The request conflicts with another request (perhaps due to using the same idempotent key). |
424 | External Dependency Failed | The request couldn't be completed due to a failure in a dependency external to Quab Express. |
429 | Too Many Requests | Too many requests hit the API too quickly. We recommend an exponential backoff of your requests. |
500, 502, 503, 504 | Server Errors | Something went wrong on Quab Express' end. (These are rare.) |
{
"status": "Error",
"message": "Authentication not set",
"action": "Add authentication to your request"
}
To create a new order, the user needs to send a POST
request to create order endpoint.
The request body should be a JSON-encoded string that specifys the details of the new delivery order.
The action to be taken by the API. The only accepted value is quote
The point of origin of the package. This can be CUSTOM_ADDRESS
,FULFILLMENT_CENTRE
or BUSINESS_ADDRESS
Details of the sender as an object. This object contains the following parameters; name
,phoneNumber
,emailAddress
and location
. It can be null only if from
parameter is set to FULFILLMENT_CENTRE
or BUSINESS_ADDRESS
.
Details of the package recipent as an object. This object contains the following parameters; name
,phoneNumber
,emailAddress
and location
. Check out Recipient object
The products to be shipped as an array. The array elements is made up ofsku
, name
and quantity
This array must have a maximum of 20 products. Check out product object.
The kind of delivery service. If the delivery order is supposed to be an Express mail use 1
, otherwise use 0
for ordinary package delivery.
The kind of delivery service. If the delivery order requires the recipient to provide a signature on delivery use 1
, otherwise use 0
for ordinary package delivery.
The name of the person that will send or pick up the package. The maximum string length is 100 characters long.
The phone number of the person or entity that sends or receieves the package. This should be a valid number that incldes the country code.
The email address of the person or entity that sends or receives the package. The emailAddress should be valid and functional. This parameter is nullable
This is the pick up or delivery point location. The object must have 3 parameters; address
, latitude
and longitude
. Check out the location object.
The name of the person that will send or pick up the package. The maximum string length is 100 characters long.
The phone number of the person or entity that sends or receieves the package. This should be a valid number that incldes the country code.
The email address of the person or entity that sends or receives the package. The emailAddress should be valid and functional. This parameter is nullable
Result of the create order request. Check out the result object
A quotation of the delivery order as an object. This will be null only if the request was not successful.
Identification string of the quote
Shipment number of the package. This is to be used on the packaging label for identification of the shipment.
Time in UNIX timestamp beyond which the quote will have expired. When this time is reached, a new quote should be generated.
Details of the charges to be imposed on the delivery charges. This object contains ID
,amount
and chargesItems
Identification string of the quote
Total amount charged in Kenyan shillings.
Array of the charges imposed on the delivery service. Check out the charge item
The name of the charge
Total amount charged in Kenyan shillings.
Unirest.setTimeouts(0, 0);
HttpResponse response = Unirest.get("https://api.quabexpress.com/order/v1/create")
.header("Content-Type", "application/json")
.header("Authorization", "Bearer 0440f5ef43b44e0904ec72516b35c2ca")
.body("{\r\n \"command\":\"quote\",\r\n \"from\":\"CUSTOM_ADDRESS\",\r\n \"sender\":{\r\n \"name\":\"Linus Laflamme\",\r\n \"phoneNumber\":\"+254741051051\",\r\n \"emailAddress\":\"[email protected]\",\r\n \"location\":{\r\n \"address\":\"South C, Nairobi\",\r\n \"latitude\":\"\",\r\n \"longitude\":\"\"\r\n }\r\n },\r\n \"recipient\":{\r\n \"name\":\"Linus Laflamme\",\r\n \"phoneNumber\":\"+254741051051\",\r\n \"emailAddress\":\"\",\r\n \"location\":{\r\n \"address\":\"Kilimani, Nairobi\",\r\n \"latitude\":\"\",\r\n \"longitude\":\"\"\r\n }\r\n },\r\n \"products\":[\r\n {\r\n \"sku\":\"GRE-1234\",\r\n \"name\":\"Blue breeze wipes\",\r\n \"quantity\": 3\r\n }\r\n ],\r\n \"isExpress\":1,\r\n \"requiresSignature\":0\r\n \r\n}")
.asString();
const myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("Authorization", "Bearer 0440f5ef43b44e0904ec72516b35c2ca");
const raw = JSON.stringify({
"command": "quote",
"from": "CUSTOM_ADDRESS",
"sender": {
"name": "Linus Laflamme",
"phoneNumber": "+254741051051",
"emailAddress": "[email protected]",
"location": {
"address": "South C, Nairobi",
"latitude": "",
"longitude": ""
}
},
"recipient": {
"name": "Linus Laflamme",
"phoneNumber": "+254741051051",
"emailAddress": "",
"location": {
"address": "Kilimani, Nairobi",
"latitude": "",
"longitude": ""
}
},
"products": [
{
"sku": "GRE-1234",
"name": "Blue breeze wipes",
"quantity": 3
}
],
"isExpress": 1,
"requiresSignature": 0
});
const requestOptions = {
method: "GET",
headers: myHeaders,
body: raw,
redirect: "follow"
};
fetch("https://api.quabexpress.com/order/v1/create", requestOptions)
.then((response) => response.text())
.then((result) => console.log(result))
.catch((error) => console.error(error));
var options = new RestClientOptions("")
{
MaxTimeout = -1,
};
var client = new RestClient(options);
var request = new RestRequest("https://api.quabexpress.com/order/v1/create", Method.Get);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Authorization", "Bearer 0440f5ef43b44e0904ec72516b35c2ca");
var body = @"{
" + "\n" +
@" ""command"":""quote"",
" + "\n" +
@" ""from"":""CUSTOM_ADDRESS"",
" + "\n" +
@" ""sender"":{
" + "\n" +
@" ""name"":""Linus Laflamme"",
" + "\n" +
@" ""phoneNumber"":""+254741051051"",
" + "\n" +
@" ""emailAddress"":""[email protected]"",
" + "\n" +
@" ""location"":{
" + "\n" +
@" ""address"":""South C, Nairobi"",
" + "\n" +
@" ""latitude"":"""",
" + "\n" +
@" ""longitude"":""""
" + "\n" +
@" }
" + "\n" +
@" },
" + "\n" +
@" ""recipient"":{
" + "\n" +
@" ""name"":""Linus Laflamme"",
" + "\n" +
@" ""phoneNumber"":""+254741051051"",
" + "\n" +
@" ""emailAddress"":"""",
" + "\n" +
@" ""location"":{
" + "\n" +
@" ""address"":""Kilimani, Nairobi"",
" + "\n" +
@" ""latitude"":"""",
" + "\n" +
@" ""longitude"":""""
" + "\n" +
@" }
" + "\n" +
@" },
" + "\n" +
@" ""products"":[
" + "\n" +
@" {
" + "\n" +
@" ""sku"":""GRE-1234"",
" + "\n" +
@" ""name"":""Blue breeze wipes"",
" + "\n" +
@" ""quantity"": 3
" + "\n" +
@" }
" + "\n" +
@" ],
" + "\n" +
@" ""isExpress"":1,
" + "\n" +
@" ""requiresSignature"":0
" + "\n" +
@"
" + "\n" +
@"}";
request.AddStringBody(body, DataFormat.Json);
RestResponse response = await client.ExecuteAsync(request);
Console.WriteLine(response.Content);
<?php
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://api.quabexpress.com/order/v1/create',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_POSTFIELDS =>'{
"command":"quote",
"from":"CUSTOM_ADDRESS",
"sender":{
"name":"Linus Laflamme",
"phoneNumber":"+254741051051",
"emailAddress":"[email protected]",
"location":{
"address":"South C, Nairobi",
"latitude":"",
"longitude":""
}
},
"recipient":{
"name":"Linus Laflamme",
"phoneNumber":"+254741051051",
"emailAddress":"",
"location":{
"address":"Kilimani, Nairobi",
"latitude":"",
"longitude":""
}
},
"products":[
{
"sku":"GRE-1234",
"name":"Blue breeze wipes",
"quantity": 3
}
],
"isExpress":1,
"requiresSignature":0
}',
CURLOPT_HTTPHEADER => array(
'Content-Type: application/json',
'Authorization: Bearer 0440f5ef43b44e0904ec72516b35c2ca'
),
));
$response = curl_exec($curl);
curl_close($curl);
echo $response;
?>
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\r\n \"command\":\"quote\",\r\n \"from\":\"CUSTOM_ADDRESS\",\r\n \"sender\":{\r\n \"name\":\"Linus Laflamme\",\r\n \"phoneNumber\":\"+254741051051\",\r\n \"emailAddress\":\"[email protected]\",\r\n \"location\":{\r\n \"address\":\"South C, Nairobi\",\r\n \"latitude\":\"\",\r\n \"longitude\":\"\"\r\n }\r\n },\r\n \"recipient\":{\r\n \"name\":\"Linus Laflamme\",\r\n \"phoneNumber\":\"+254741051051\",\r\n \"emailAddress\":\"\",\r\n \"location\":{\r\n \"address\":\"Kilimani, Nairobi\",\r\n \"latitude\":\"\",\r\n \"longitude\":\"\"\r\n }\r\n },\r\n \"products\":[\r\n {\r\n \"sku\":\"GRE-1234\",\r\n \"name\":\"Blue breeze wipes\",\r\n \"quantity\": 3\r\n }\r\n ],\r\n \"isExpress\":1,\r\n \"requiresSignature\":0\r\n \r\n}");
Request request = new Request.Builder()
.url("https://api.quabexpress.com/order/v1/create")
.method("GET", body)
.addHeader("Content-Type", "application/json")
.addHeader("Authorization", "Bearer 0440f5ef43b44e0904ec72516b35c2ca")
.build();
Response response = client.newCall(request).execute();
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET");
curl_easy_setopt(curl, CURLOPT_URL, "https://api.quabexpress.com/order/v1/create");
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "https");
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Content-Type: application/json");
headers = curl_slist_append(headers, "Authorization: Bearer 0440f5ef43b44e0904ec72516b35c2ca");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
const char *data = "{\r\n \"command\":\"quote\",\r\n \"from\":\"CUSTOM_ADDRESS\",\r\n \"sender\":{\r\n \"name\":\"Linus Laflamme\",\r\n \"phoneNumber\":\"+254741051051\",\r\n \"emailAddress\":\"[email protected]\",\r\n \"location\":{\r\n \"address\":\"South C, Nairobi\",\r\n \"latitude\":\"\",\r\n \"longitude\":\"\"\r\n }\r\n },\r\n \"recipient\":{\r\n \"name\":\"Linus Laflamme\",\r\n \"phoneNumber\":\"+254741051051\",\r\n \"emailAddress\":\"\",\r\n \"location\":{\r\n \"address\":\"Kilimani, Nairobi\",\r\n \"latitude\":\"\",\r\n \"longitude\":\"\"\r\n }\r\n },\r\n \"products\":[\r\n {\r\n \"sku\":\"GRE-1234\",\r\n \"name\":\"Blue breeze wipes\",\r\n \"quantity\": 3\r\n }\r\n ],\r\n \"isExpress\":1,\r\n \"requiresSignature\":0\r\n \r\n}";
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
res = curl_easy_perform(curl);
curl_slist_free_all(headers);
}
curl_easy_cleanup(curl);
let parameters = "{\r\n \"command\":\"quote\",\r\n \"from\":\"CUSTOM_ADDRESS\",\r\n \"sender\":{\r\n \"name\":\"Linus Laflamme\",\r\n \"phoneNumber\":\"+254741051051\",\r\n \"emailAddress\":\"[email protected]\",\r\n \"location\":{\r\n \"address\":\"South C, Nairobi\",\r\n \"latitude\":\"\",\r\n \"longitude\":\"\"\r\n }\r\n },\r\n \"recipient\":{\r\n \"name\":\"Linus Laflamme\",\r\n \"phoneNumber\":\"+254741051051\",\r\n \"emailAddress\":\"\",\r\n \"location\":{\r\n \"address\":\"Kilimani, Nairobi\",\r\n \"latitude\":\"\",\r\n \"longitude\":\"\"\r\n }\r\n },\r\n \"products\":[\r\n {\r\n \"sku\":\"GRE-1234\",\r\n \"name\":\"Blue breeze wipes\",\r\n \"quantity\": 3\r\n }\r\n ],\r\n \"isExpress\":1,\r\n \"requiresSignature\":0\r\n \r\n}"
let postData = parameters.data(using: .utf8)
var request = URLRequest(url: URL(string: "https://api.quabexpress.com/order/v1/create")!,timeoutInterval: Double.infinity)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("Bearer 0440f5ef43b44e0904ec72516b35c2ca", forHTTPHeaderField: "Authorization")
request.httpMethod = "GET"
request.httpBody = postData
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else {
print(String(describing: error))
return
}
print(String(data: data, encoding: .utf8)!)
}
task.resume()
{
"result": {
"status": "Success",
"message": "Successful",
"action": ""
},
"data": {
"quotationID": "U8KH4L",
"shipmentID": "SM6W5A",
"expiry": 1754340187,
"charges": {
"ID": "U8KH4L",
"amount": 500,
"chargeItems": [
{
"name": "Shipping charges",
"amount": "500.00"
},
{
"name": "Discount",
"amount": "0.00"
}
]
}
}
}
Once the user has received the quotation of the shipment delivery service, the user will have to confirm the quotation before it's expiry. The order will only be processed after confirmation. This request does the confirmation of the order request.
To confirm an order, a post request is sent to the API endpoint.
The action to be taken by the API. The only accepted value is confirm
The point of origin of the package. This can be CUSTOM_ADDRESS
,FULFILLMENT_CENTRE
or BUSINESS_ADDRESS
Result of the create order request. Check out the result object
A quotation of the delivery order as an object. This will be null only if the request was not successful.
var https = require('follow-redirects').https;
var fs = require('fs');
var options = {
'method': 'GET',
'hostname': 'api.quabexpress.com',
'path': '/order/v1/confirm',
'headers': {
'Content-Type': 'application/json',
'Authorization': 'Bearer 0440f5ef43b44e0904ec72516b35c2ca'
},
'maxRedirects': 20
};
var req = https.request(options, function (res) {
var chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function (chunk) {
var body = Buffer.concat(chunks);
console.log(body.toString());
});
res.on("error", function (error) {
console.error(error);
});
});
var postData = JSON.stringify({
"command": "confirm",
"quotation": "U8KH4L"
});
req.write(postData);
req.end();
const myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("Authorization", "Bearer 0440f5ef43b44e0904ec72516b35c2ca");
const raw = JSON.stringify({
"command": "confirm",
"quotation": "U8KH4L"
});
const requestOptions = {
method: "GET",
headers: myHeaders,
body: raw,
redirect: "follow"
};
fetch("https://api.quabexpress.com/order/v1/confirm", requestOptions)
.then((response) => response.text())
.then((result) => console.log(result))
.catch((error) => console.error(error));
var options = new RestClientOptions("")
{
MaxTimeout = -1,
};
var client = new RestClient(options);
var request = new RestRequest("https://api.quabexpress.com/order/v1/confirm", Method.Get);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Authorization", "Bearer 0440f5ef43b44e0904ec72516b35c2ca");
var body = @"{
" + "\n" +
@" ""command"":""confirm"",
" + "\n" +
@" ""quotation"":""U8KH4L""
" + "\n" +
@"}";
request.AddStringBody(body, DataFormat.Json);
RestResponse response = await client.ExecuteAsync(request);
Console.WriteLine(response.Content);
<?php
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://api.quabexpress.com/order/v1/confirm',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_POSTFIELDS =>'{
"command":"confirm",
"quotation":"U8KH4L"
}',
CURLOPT_HTTPHEADER => array(
'Content-Type: application/json',
'Authorization: Bearer 0440f5ef43b44e0904ec72516b35c2ca'
),
));
$response = curl_exec($curl);
curl_close($curl);
echo $response;
?>
Unirest.setTimeouts(0, 0);
HttpResponse<String> response = Unirest.get("https://api.quabexpress.com/order/v1/confirm")
.header("Content-Type", "application/json")
.header("Authorization", "Bearer 0440f5ef43b44e0904ec72516b35c2ca")
.body("{\r\n \"command\":\"confirm\",\r\n \"quotation\":\"U8KH4L\"\r\n}")
.asString();
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET");
curl_easy_setopt(curl, CURLOPT_URL, "https://api.quabexpress.com/order/v1/confirm");
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "https");
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Content-Type: application/json");
headers = curl_slist_append(headers, "Authorization: Bearer 0440f5ef43b44e0904ec72516b35c2ca");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
const char *data = "{\r\n \"command\":\"confirm\",\r\n \"quotation\":\"U8KH4L\"\r\n}";
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
res = curl_easy_perform(curl);
curl_slist_free_all(headers);
}
curl_easy_cleanup(curl);
let parameters = "{\r\n \"command\":\"confirm\",\r\n \"quotation\":\"U8KH4L\"\r\n}"
let postData = parameters.data(using: .utf8)
var request = URLRequest(url: URL(string: "https://api.quabexpress.com/order/v1/confirm")!,timeoutInterval: Double.infinity)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("Bearer 0440f5ef43b44e0904ec72516b35c2ca", forHTTPHeaderField: "Authorization")
request.httpMethod = "GET"
request.httpBody = postData
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else {
print(String(describing: error))
return
}
print(String(data: data, encoding: .utf8)!)
}
task.resume()
{
"result": {
"status": "Success",
"message": "Successful",
"action": ""
},
"data": {
"order": "R795RU2",
"quotation": "U8KH4L",
"shipment": "SM6W5A",
"tracking": "QE0003936761KE",
"recipient": {
"name": "Linus Laflamme",
"phone": "+254741051051",
"address": "Kilimani, Nairobi"
}
}
}
Webhooks allow you to set up a notification system that can be used to receive updates on certain requests made to the Quab Express API.
After setting up a webhook url through your dashboard, our server will make requests to the url with payloads of the relevant events that take place to your account and/or order.
When your webhook URL receives an event, it needs to parse and acknowledge the event. Acknowledging an event means returning a 200 OK
in the HTTP header. Without a 200 OK in the response header, events are sent for the next 72 hours:
If you have extra tasks in your webhook function, you should return a 200 OK
response immediately. Long-running tasks lead to a request timeout and an automatic error response from your server. Without a 200 OK
response, the retry as described in the proceeding paragraph.
Since your webhook URL is publicly available, you need to verify that events originate from Quab Express and not a bad actor. There are two ways to ensure events to your webhook URL are from Quab Express:
Events sent from Quab Express carry the x-paystack-signature header
. The value of this header is a HMAC SHA256 signature of the event payload signed using your secret key. Verifying the header signature should be done before processing the event:
When your webhook URL receives an event, it needs to parse and acknowledge the event. Acknowledging an event means returning a 200 OK
in the HTTP header. Without a 200 OK in the response header, events are sent for the next 72 hours:
Here are the events we currently raise. We would add more to this list as we hook into more actions in the future.
Event | Description |
---|---|
order.placed |
OK |
order.confirmed |
OK |
order.processed |
OK |
order.dispatched |
OK |
order.delivered |
OK |
order.returned |
OK |
order.cancelled |
OK |
// Using Express
app.post("/my/webhook/url", function(req, res) {
// Retrieve the request's body
const event = req.body;
// Do something with event
res.send(200);
});
const http = require('http');
// Create an HTTP server
const server = http.createServer((req, res) => {
let body = '';
// Retrieve the request's body
req.on('data', chunk => {
body += chunk.toString(); // Convert Buffer to string
});
req.on('end', () => {
try {
// Parse the input as JSON
const event = JSON.parse(body);
// Do something with event
console.log("Received event:", event); // Example: Print the event
// Set the HTTP response code
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ status: 'success' })); // Respond with a success message
} catch (error) {
console.error("Error parsing JSON:", error);
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Invalid JSON' })); // Respond with an error message
}
});
});
// Server listens on port 3000
server.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
public class Program
{
public static async Task Main(string[] args)
{
// Set up an HTTP listener
var listener = new HttpListener();
listener.Prefixes.Add("http://localhost:8080/"); // Replace with your desired URL
try
{
listener.Start();
Console.WriteLine("Listening for requests on http://localhost:8080/");
while (true)
{
// Wait for a request
var context = await listener.GetContextAsync();
var request = context.Request;
var response = context.Response;
// Read the request body
string requestBody;
using (var reader = new StreamReader(request.InputStream, Encoding.UTF8))
{
requestBody = await reader.ReadToEndAsync();
}
// Process the request body as JSON
try
{
// Deserialize the JSON
var eventData = JsonSerializer.Deserialize<JsonElement>(requestBody);
// Do something with eventData
Console.WriteLine("Received event:");
Console.WriteLine(JsonSerializer.Serialize(eventData, new JsonSerializerOptions { WriteIndented = true })); // Pretty print the JSON
// Set the response
response.StatusCode = (int)HttpStatusCode.OK; // 200 OK
response.ContentType = "application/json";
var responseString = JsonSerializer.Serialize(new { status = "success" }); // Create a success response
byte[] buffer = Encoding.UTF8.GetBytes(responseString);
response.ContentLength64 = buffer.Length;
var output = response.OutputStream;
await output.WriteAsync(buffer, 0, buffer.Length);
output.Close();
}
catch (JsonException ex)
{
// Handle JSON parsing errors
Console.WriteLine($"Error parsing JSON: {ex.Message}");
response.StatusCode = (int)HttpStatusCode.BadRequest; // 400 Bad Request
response.ContentType = "application/json";
var errorResponse = JsonSerializer.Serialize(new { error = "Invalid JSON" });
byte[] buffer = Encoding.UTF8.GetBytes(errorResponse);
response.ContentLength64 = buffer.Length;
var output = response.OutputStream;
await output.WriteAsync(buffer, 0, buffer.Length);
output.Close();
}
catch (Exception ex)
{
// Handle other errors
Console.WriteLine($"An error occurred: {ex.Message}");
response.StatusCode = (int)HttpStatusCode.InternalServerError; // 500 Internal Server Error
response.ContentType = "application/json";
var errorResponse = JsonSerializer.Serialize(new { error = "Internal Server Error" });
byte[] buffer = Encoding.UTF8.GetBytes(errorResponse);
response.ContentLength64 = buffer.Length;
var output = response.OutputStream;
await output.WriteAsync(buffer, 0, buffer.Length);
output.Close();
}
}
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
finally
{
listener.Stop();
}
}
}
<?php
// Retrieve the request's body and parse it as JSON
$input = @file_get_contents("php://input");
$event = json_decode($input);
// Do something with $event
http_response_code(200); // PHP 5.4 or greater
?>
JSONObject json = new JSONObject();
json.put("key", "value");
// Sending a POST request
HttpResponse<JsonNode> response = Unirest.post("http://localhost:3000/")
.header("Content-Type", "application/json")
.body(json)
.asJson();
// Check the response status
if (response.getStatus() == 200) {
System.out.println("Success: " + response.getBody());
} else {
System.out.println("Error: " + response.getStatus() + " - " + response.getBody());
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h> // For sending an HTTP response - you may need to install libcurl
// Structure to hold the response data (for libcurl)
struct ResponseData {
char *data;
size_t size;
};
// Callback function for libcurl to write the response data
size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) {
size_t realsize = size * nmemb;
struct ResponseData *resp = (struct ResponseData *)userp;
char *ptr = realloc(resp->data, resp->size + realsize + 1);
if (ptr == NULL) {
// Out of memory!
return 0;
}
resp->data = ptr;
memcpy(&(resp->data[resp->size]), contents, realsize);
resp->size += realsize;
resp->data[resp->size] = 0;
return realsize;
}
int main() {
char *input = NULL;
size_t input_size = 0;
int ch;
// Read input from stdin (standard input)
// This is a basic way to read the input. You might need to adapt this
// depending on how the input is provided (e.g., from a file, network).
while ((ch = getchar()) != EOF) {
input = realloc(input, input_size + 1);
if (input == NULL) {
perror("realloc failed");
free(input);
return 1;
}
input[input_size++] = ch;
}
if (input_size > 0) {
input[input_size] = '\0'; // Null-terminate the input string
}
// Parse the input as JSON (simplified - you'll likely want a proper JSON library)
// This is a placeholder. You'll need a JSON parsing library (e.g., cJSON, Jansson)
// to correctly parse the JSON data. I'll show a very basic example.
if (input != NULL && input_size > 0) {
printf("Received input: %s\n", input); // Basic output
// **IMPORTANT:** Replace this with a proper JSON parsing library!
// For example, using cJSON:
// cJSON *json = cJSON_Parse(input);
// if (json != NULL) {
// // Access JSON elements here
// cJSON_Delete(json);
// } else {
// fprintf(stderr, "Error parsing JSON\n");
// }
} else {
fprintf(stderr, "No input received.\n");
}
// Set the HTTP response code (requires libcurl or similar)
// This example *sends* an HTTP response. If you're just processing input,
// you might not need this.
CURL *curl;
CURLcode res;
struct ResponseData response;
response.data = malloc(1); // Allocate initial memory
response.size = 0;
response.data[0] = '\0';
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, "your_callback_url_here"); // Replace with your URL
curl_easy_setopt(curl, CURLOPT_POST, 1L); // Use POST method
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"status\": \"success\"}"); // Replace with your data
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
} else {
//printf("Response: %s\n", response.data); // Print the response (if any)
}
curl_easy_cleanup(curl);
} else {
fprintf(stderr, "curl_easy_init() failed\n");
}
free(response.data); // Free the allocated memory for the response
free(input); // Free the allocated input buffer
return 0;
}
import Foundation
// Retrieve the request's body
if let input = String(data: FileHandle.standardInput.availableData, encoding: .utf8) {
// Parse the input as JSON
if let eventData = input.data(using: .utf8),
let event = try? JSONSerialization.jsonObject(with: eventData, options: []) {
// Do something with event
if let eventDictionary = event as? [String: Any] {
// Access JSON data using eventDictionary
print("Received event: \(eventDictionary)") // Example: Print the event
} else if let eventArray = event as? [Any] {
// Access JSON data using eventArray
print("Received event: \(eventArray)") // Example: Print the event
} else {
print("Received event: \(event)") // If it's not a dictionary or array
}
} else {
print("Error: Could not decode JSON.")
}
} else {
print("Error: Could not read input.")
}
const crypto = require('crypto');
const secret = process.env.SECRET_KEY;
// Using Express
app.post("/my/webhook/url", function(req, res) {
//validate event
const hash = crypto.createHmac('sha256', secret).update(JSON.stringify(req.body)).digest('hex');
if (hash == req.headers['x-quab-express-signature']) {
// Retrieve the request's body
const event = req.body;
// Do something with event
}
res.send(200);
});
using System;
using System.Security.Cryptography;
using System.Text;
using Newtonsoft.Json.Linq;
namespace HMacExample
{
class Program {
static void Main(string[] args) {
String key = "YOUR_SECRET_KEY"; //replace with your quab express secret_key
String jsonInput = "{"paystack":"request","body":"to_string"}"; //the json input
String inputString = Convert.ToString(new JValue(jsonInput));
String result = "";
byte[] secretkeyBytes = Encoding.UTF8.GetBytes(key);
byte[] inputBytes = Encoding.UTF8.GetBytes(inputString);
using (var hmac = new HMACSHA256(secretkeyBytes))
{
byte[] hashValue = hmac.ComputeHash(inputBytes);
result = BitConverter.ToString(hashValue).Replace("-", string.Empty);;
}
Console.WriteLine(result);
String xQuabExpressSignature = ""; //put in the request's header value for x-quab-express-signature
if(result.ToLower().Equals(xQuabExpressSignature)) {
// you can trust the event, it came from quab express
// respond with the http 200 response immediately before attempting to process the response
//retrieve the request body, and deliver value to the customer
} else {
// this isn't from Quab Express, ignore it
}
}
}
}
<?php
// only a post with quab express signature header gets our attention
if ((strtoupper($_SERVER['REQUEST_METHOD']) != 'POST' ) || !array_key_exists('HTTP_X_QUAB_EXPRESS_SIGNATURE', $_SERVER) )
exit();
// Retrieve the request's body
$input = @file_get_contents("php://input");
define('QUAB_EXPRESS_SECRET_KEY','SECRET_KEY');
// validate event do all at once to avoid timing attack
if($_SERVER['HTTP_X_QUAB_EXPRESS_SIGNATURE'] !== hash_hmac('sha256', $input, QUAB_EXPRESS_SECRET_KEY))
exit();
http_response_code(200);
// parse event (which is json string) as object
// Do something - that will not take long - with $event
$event = json_decode($input);
exit();
?>
package hmacexample;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import org.json.JSONException;
import org.json.JSONObject;
public class HMacExample {
public static void main(String[] args) throws UnsupportedEncodingException, InvalidKeyException, NoSuchAlgorithmException, JSONException {
//first you verify that this request came from quab express
String key = "YOUR_SECRET_KEY"; //replace with your quab express secret_key
String rawJson = "{\"paystack\":\"request\",\"body":\"to_string\"}";
JSONObject body = new JSONObject(rawJson);
String result = "";
String HMAC_SHA256 = "HmacSHA256";
String xQuabExpressSignature = ""; //put in the request's header value for x-quab-express-signature
byte [] byteKey = key.getBytes("UTF-8");
SecretKeySpec keySpec = new SecretKeySpec(byteKey, HMAC_SHA256);
Mac sha256_HMAC = Mac.getInstance(HMAC_SHA256);
sha256_HMAC.init(keySpec);
byte [] mac_data = sha256_HMAC.
doFinal(body.getBytes("UTF-8"));
result = DatatypeConverter.printHexBinary(mac_data);
if(result.toLowerCase().equals(xQuabExpressSignature)) {
// you can trust the event, it came from paystack
// respond with the http 200 response immediately before attempting to process the response
//retrieve the request body, and deliver value to the customer
}else{
// this isn't from Quab Express, ignore it
}
}
}