Pulses are webhook notifications that allow you to receive real-time HTTP POST requests when specific events occur in your Chariow store. Instead of constantly polling the API, Pulses push event data to your configured endpoint URL as soon as something happens, enabling you to build responsive integrations and automations.
How Pulses Work
Event Occurs
An event happens in your store (e.g., a sale is completed).
Pulse Triggered
Chariow sends an HTTP POST request to your configured endpoint.
Process the Event
Your server receives the payload and processes it.
Acknowledge Receipt
Your server returns a 2xx status code to confirm receipt.
Setting Up Pulses
You can configure Pulses in several ways:
Via Store Dashboard
Go to Automation → Pulses
Click Add Pulse
Enter your webhook endpoint URL (must be HTTPS)
Select the events you want to receive
Optionally select specific products (leave empty for all products)
Save your Pulse
Your Pulse endpoint must be accessible via HTTPS. HTTP endpoints are not supported for security reasons.
Pulse Events
Pulses support the following events. When an event fires, the webhook payload contains an event field with the event value.
Sale Events
Event Value Label Description successful.saleSuccessful Sale Triggers when a sale is completed abandoned.saleAbandoned Sale Triggers when a sale is abandoned failed.saleFailed Sale Triggers when a sale fails
License Events
Event Value Label Description license.activatedLicense Activated Triggers when a license is activated license.expiredLicense Expired Triggers when a license expires license.issuedLicense Issued Triggers when a license is issued to a customer license.revokedLicense Revoked Triggers when a license is revoked
Affiliate Events
Event Value Label Description affiliate.joinedAffiliate Joined Triggers when a new affiliate joins your store
You can configure multiple events for a single Pulse URL. When any of the selected events occur, Chariow will send a webhook notification to your endpoint with the corresponding event value.
Pulse Payload
When a configured event occurs, Chariow sends an HTTP POST request to your webhook URL with event data in the request body. The exact payload structure depends on the event type.
Pulse payloads contain comprehensive event data, including details about the entity (sale, license, etc.), customer information, product details, and store context. The payload structure varies by event type to provide relevant information for each event.
Successful Sale Example
When a successful sale occurs, Chariow sends a webhook with the successful.sale event:
{
"event" : "successful.sale" ,
"sale" : {
"id" : "sal_xyz789abc" ,
"amount" : {
"amount" : 9900 ,
"formatted" : "$99.00" ,
"currency" : "USD"
},
"original_amount" : {
"amount" : 9900 ,
"formatted" : "$99.00" ,
"currency" : "USD"
},
"discount_amount" : {
"amount" : 0 ,
"formatted" : "$0.00" ,
"currency" : "USD"
},
"settlement" : {
"amount" : {
"amount" : 8910 ,
"formatted" : "$89.10" ,
"currency" : "USD"
},
"due_at" : "2025-01-22T10:30:00+00:00" ,
"done_at" : null ,
"service_fee" : {
"amount" : 495 ,
"formatted" : "$4.95" ,
"currency" : "USD"
},
"payment_fee" : {
"amount" : 495 ,
"formatted" : "$4.95" ,
"currency" : "USD"
},
"fee" : {
"amount" : 990 ,
"formatted" : "$9.90" ,
"currency" : "USD"
}
},
"status" : "completed" ,
"created_at" : "2025-01-15T10:30:00+00:00" ,
"custom_fields" : null ,
"custom_metadata" : { "order_ref" : "ORD-123" },
"completed_at" : "2025-01-15T10:32:00+00:00" ,
"abandoned_at" : null ,
"failed_at" : null
},
"product" : {
"id" : "prd_def456" ,
"name" : "Premium Course" ,
"url" : "https://mystore.mychariow.com/p/premium-course" ,
"price" : {
"amount" : 9900 ,
"formatted" : "$99.00" ,
"currency" : "USD"
}
},
"customer" : {
"id" : "cus_abc123" ,
"name" : "John Doe" ,
"first_name" : "John" ,
"last_name" : "Doe" ,
"email" : "[email protected] " ,
"phone" : "+1234567890" ,
"country" : "US"
},
"affiliate" : null ,
"store" : {
"id" : "str_xyz789" ,
"name" : "My Digital Store" ,
"url" : "https://mystore.mychariow.com"
},
"checkout" : {
"url" : "https://mystore.mychariow.com/checkout/sal_xyz789abc"
}
}
License Activated Example
When a license is activated, Chariow sends a webhook with the license.activated event:
{
"event" : "license.activated" ,
"license" : {
"id" : "lic_ghi789def" ,
"key" : "ABCD-1234-EFGH-5678" ,
"status" : "active" ,
"source_type" : "sale" ,
"activation_count" : 1 ,
"max_activations" : 5 ,
"activated_at" : "2025-01-15T10:35:00+00:00" ,
"expires_at" : "2026-01-15T10:35:00+00:00" ,
"expired_at" : null ,
"revoked_at" : null ,
"created_at" : "2025-01-15T10:30:00+00:00"
},
"product" : {
"id" : "prd_def456" ,
"name" : "Pro Software License" ,
"url" : "https://mystore.mychariow.com/p/pro-software"
},
"customer" : {
"id" : "cus_abc123" ,
"name" : "John Doe" ,
"first_name" : "John" ,
"last_name" : "Doe" ,
"email" : "[email protected] " ,
"phone" : "+1234567890" ,
"country" : "US"
},
"store" : {
"id" : "str_xyz789" ,
"name" : "My Digital Store" ,
"url" : "https://mystore.mychariow.com"
}
}
Affiliate Joined Example
When a new affiliate joins your store, Chariow sends a webhook with the affiliate.joined event:
{
"event" : "affiliate.joined" ,
"affiliate" : {
"id" : "saff_xyz789abc" ,
"account" : {
"id" : "aff_abc123def" ,
"code" : "CREATOR123" ,
"pseudo" : "creative_studio" ,
"email" : "[email protected] " ,
"first_name" : "John" ,
"last_name" : "Doe" ,
"name" : "John Doe" ,
"country" : {
"code" : "US" ,
"name" : "United States"
},
"phone" : {
"country_code" : "US" ,
"formatted" : "+1 234 567 890"
}
},
"source" : "invitation" ,
"status" : "active" ,
"joined_at" : "2025-01-15T10:40:00+00:00"
},
"store" : {
"id" : "str_xyz789" ,
"name" : "My Digital Store" ,
"url" : "https://mystore.mychariow.com"
}
}
The exact payload structure may include additional fields depending on the event type and context. Always check the actual payload received at your endpoint for the complete data structure.
Handling Pulses
Basic Example (Node.js/Express)
const express = require ( 'express' );
const app = express ();
app . post ( '/webhooks/chariow' , express . json (), ( req , res ) => {
const payload = req . body ;
const event = payload . event ;
switch ( event ) {
case 'successful.sale' :
handleSuccessfulSale ( payload . sale , payload . product , payload . customer , payload . store );
break ;
case 'abandoned.sale' :
handleAbandonedSale ( payload . sale , payload . store );
break ;
case 'failed.sale' :
handleFailedSale ( payload . sale , payload . store );
break ;
case 'license.activated' :
handleLicenseActivated ( payload . license , payload . product , payload . customer );
break ;
case 'license.expired' :
handleLicenseExpired ( payload . license , payload . customer );
break ;
case 'license.issued' :
handleLicenseIssued ( payload . license , payload . product , payload . customer );
break ;
case 'license.revoked' :
handleLicenseRevoked ( payload . license , payload . customer );
break ;
case 'affiliate.joined' :
handleAffiliateJoined ( payload . affiliate , payload . store );
break ;
default :
console . log ( `Unhandled event type: ${ event } ` );
}
// Always return 200 to acknowledge receipt
res . status ( 200 ). send ( 'OK' );
});
function handleSuccessfulSale ( sale , store ) {
console . log ( `Sale completed: ${ sale . id } in store ${ store . name } ` );
// Add customer to CRM, send confirmation email, grant access, etc.
}
function handleLicenseActivated ( license , customer ) {
console . log ( `License activated: ${ license . key } for ${ customer . email } ` );
// Log activation, update internal records, notify customer, etc.
}
function handleAffiliateJoined ( affiliate , store ) {
console . log ( `New affiliate joined: ${ affiliate . account . code } in store ${ store . name } ` );
// Send welcome email, create affiliate dashboard access, notify team, etc.
}
app . listen ( 3000 , () => console . log ( 'Webhook server running on port 3000' ));
PHP Example
<? php
// Read the raw POST data
$payload = file_get_contents ( 'php://input' );
$data = json_decode ( $payload , true );
$event = $data [ 'event' ] ?? null ;
switch ( $event ) {
case 'successful.sale' :
handleSuccessfulSale ( $data [ 'sale' ], $data [ 'product' ], $data [ 'customer' ], $data [ 'store' ]);
break ;
case 'abandoned.sale' :
handleAbandonedSale ( $data [ 'sale' ], $data [ 'store' ]);
break ;
case 'failed.sale' :
handleFailedSale ( $data [ 'sale' ], $data [ 'store' ]);
break ;
case 'license.activated' :
handleLicenseActivated ( $data [ 'license' ], $data [ 'product' ], $data [ 'customer' ]);
break ;
case 'license.expired' :
handleLicenseExpired ( $data [ 'license' ], $data [ 'customer' ]);
break ;
case 'license.issued' :
handleLicenseIssued ( $data [ 'license' ], $data [ 'product' ], $data [ 'customer' ]);
break ;
case 'license.revoked' :
handleLicenseRevoked ( $data [ 'license' ], $data [ 'customer' ]);
break ;
case 'affiliate.joined' :
handleAffiliateJoined ( $data [ 'affiliate' ], $data [ 'store' ]);
break ;
default :
error_log ( "Unhandled event type: " . $event );
}
// Always return 200 to acknowledge receipt
http_response_code ( 200 );
echo 'OK' ;
function handleSuccessfulSale ( $sale , $store ) {
error_log ( "Sale completed: { $sale ['id']} in store { $store ['name']}" );
// Add customer to CRM, send confirmation email, grant access, etc.
}
function handleLicenseActivated ( $license , $customer ) {
error_log ( "License activated: { $license ['key']} for { $customer ['email']}" );
// Log activation, update internal records, notify customer, etc.
}
function handleAffiliateJoined ( $affiliate , $store ) {
error_log ( "New affiliate joined: { $affiliate ['account']['code']} in store { $store ['name']}" );
// Send welcome email, create affiliate dashboard access, notify team, etc.
}
Retry Policy
If your endpoint doesn’t respond with a 2xx status code, Chariow will retry the Pulse:
Attempt Delay 1st retry 1 minute 2nd retry 5 minutes 3rd retry 30 minutes 4th retry 2 hours 5th retry 24 hours
After 5 failed attempts, the Pulse is marked as failed.
Ensure your Pulse endpoint responds quickly (within 30 seconds). Long-running processes should be handled asynchronously.
Best Practices
Return a 200 response immediately, then process the Pulse asynchronously to avoid timeouts: app . post ( '/webhooks/chariow' , ( req , res ) => {
// Acknowledge receipt immediately
res . status ( 200 ). send ( 'OK' );
// Process asynchronously (e.g., queue job, background worker)
queuePulseProcessing ( req . body );
});
Due to retry logic, Pulses may be sent multiple times. Implement idempotency by tracking processed events: const processedEvents = new Set ();
async function processPulse ( payload ) {
// Create unique identifier from pulse data
const uniqueId = ` ${ payload . event } _ ${ payload . sale ?. id || payload . license ?. id || payload . affiliate ?. id } ` ;
if ( processedEvents . has ( uniqueId )) {
console . log ( 'Duplicate pulse, skipping' );
return ;
}
processedEvents . add ( uniqueId );
// Process the pulse...
}
Always use HTTPS for your Pulse endpoint to ensure data is encrypted in transit. Chariow will reject HTTP endpoints for security reasons.
Monitor your Pulse endpoint for failures and errors. Check your store dashboard regularly for failed Pulse deliveries and investigate the root cause.
For high-volume stores, consider creating separate Pulses for different products to make processing more efficient and organised.
Testing Pulses
Use the Pulse testing feature in your dashboard:
Go to Automation → Pulses
Click on your Pulse
Click Send Test Event
Select an event type
Check your endpoint received the test payload
For local development, use a service like ngrok to expose your local server to the internet.
Managing Pulses via API
You can programmatically manage your Pulses using the Chariow Public API:
List All Pulses
curl -X GET "https://api.chariow.com/v1/pulses" \
-H "Authorization: Bearer YOUR_API_KEY"
Get a Specific Pulse
curl -X GET "https://api.chariow.com/v1/pulses/pulse_abc123xyz" \
-H "Authorization: Bearer YOUR_API_KEY"
Filter Pulses
You can filter pulses by URL or event type using the search parameter:
curl -X GET "https://api.chariow.com/v1/pulses?search=successful_sale" \
-H "Authorization: Bearer YOUR_API_KEY"