Learn how to integrate Legion Hand Technologies widgets into your website.
Legion Hand Technologies widgets can be integrated into your website using three different methods:
Each method has its own advantages depending on your technical requirements and integration preferences.
The simplest way to embed widgets is using iframes. This method provides good isolation and works with any website.
<!-- Profile Widget --> <iframe src="https://your-domain.com/widget/profile" width="100%" height="400" frameborder="0"> </iframe> <!-- Campaigns Carousel Widget --> <iframe src="https://your-domain.com/widget/campaigns-carousel" width="100%" height="320" frameborder="0"> </iframe>
<div style="position: relative; width: 100%; height: 0; padding-bottom: 56.25%;">
<iframe
src="https://your-domain.com/widget/profile"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"
frameborder="0">
</iframe>
</div>Web components provide better integration with modern web applications and allow for more customization.
<!-- For Profile Widget --> <script src="https://your-domain.com/js/profile-widget.js"></script> <!-- For Campaigns Carousel Widget --> <script src="https://your-domain.com/js/campaigns-carousel-widget.js"></script>
<!-- Profile Widget --> <legion-profile-widget user-id="123" theme="light" show-contact="true"> </legion-profile-widget> <!-- Campaigns Carousel Widget --> <legion-campaigns-carousel max-campaigns="5" theme="light" height="280" show-expiration="true"> </legion-campaigns-carousel>
Profile Widget:
user-id: The user ID to displaytheme: "light" or "dark" themeshow-contact: Show contact informationwidth: Widget width (default: 100%)height: Widget height (default: auto)Campaigns Carousel Widget:
max-campaigns: Number of campaigns to display (1-10, default: 5)theme: "light" or "dark" themeheight: Widget height in pixels (200-500, default: 280)show-expiration: Show campaign expiration dates (default: true)base-url: Base URL for campaign links (auto-detected)api-key: Optional API key for authenticated requestsTo open your Community Page when users click a social grid image, addcommunity-url to <legion-social> (withlayout="grid"). The widget will open your page in a new tab and append postId, mediaIndex,gridIndex, utm_source,utm_medium, utm_campaign.
<script src="https://your-domain.com/js/social-widget.js"></script> <legion-social layout="grid" rows="3" cols="3" api-url="https://your-domain.com" community-url="https://your-site.com/community"> </legion-social>
Community Page integration checklist
data-post-id or predictable id).<script>
document.addEventListener('DOMContentLoaded', () => {
const p = new URLSearchParams(window.location.search);
const postId = p.get('postId');
const mediaIndex = parseInt(p.get('mediaIndex') || '0', 10);
if (!postId) return;
let el = document.querySelector('[data-post-id="' + postId + '"]') ||
document.getElementById('post-' + postId);
const tryFocus = (n = 10) => {
if (el) {
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
// If applicable, set your carousel active index = mediaIndex
} else if (n > 0) {
setTimeout(() => {
el = document.querySelector('[data-post-id="' + postId + '"]') ||
document.getElementById('post-' + postId);
tryFocus(n - 1);
}, 300);
}
};
tryFocus();
});
</script>For advanced integration, use our JavaScript API to programmatically control widgets.
<script src="https://your-domain.com/js/legion-sdk.js"></script>
<script>
const legion = new LegionSDK({
apiKey: 'your-api-key',
baseUrl: 'https://your-domain.com'
});
</script>const profileWidget = legion.createWidget('profile', {
container: '#profile-container',
userId: '123',
theme: 'light',
onLoad: () => console.log('Widget loaded'),
onError: (error) => console.error('Widget error:', error)
});// Update widget data
profileWidget.update({ userId: '456' });
// Refresh widget
profileWidget.refresh();
// Destroy widget
profileWidget.destroy();When a task verification is submitted, a webhook is sent to notify external systems about the verification details and status.
The webhook endpoint and authentication are configured through environment variables:
VERIFICATION_WEBHOOK_URL: The URL where the verification webhook will be sentVERIFICATION_WEBHOOK_SECRET: The secret token used for webhook authenticationMethod: POST
Headers:
Content-Type: application/json Authorization: Bearer ${VERIFICATION_WEBHOOK_SECRET}
The webhook sends a JSON payload with the following structure:
const webhookData = { // Verification Information verificationId: string, // MongoDB ObjectId of the verification userId: string, // MongoDB ObjectId of the user userEmail: string | null, // Email of the user who submitted claimId: string, // MongoDB ObjectId of the claim taskId: string, // MongoDB ObjectId of the task approved: boolean, // Whether verification is approved rejected: boolean, // Whether verification is rejected rejectedReason: string | null, // Reason for rejection if rejected reviewed: boolean, // Whether verification is reviewed started: boolean, // Whether verification is started startedAt: DateTime | null, // When verification was started submitted: boolean, // Whether verification is submitted submittedAt: DateTime | null, // When verification was submitted text: string | null, // Text content if any link: string | null, // Link content if any lat: number | null, // Latitude if location provided long: number | null, // Longitude if location provided createdAt: DateTime, // Creation timestamp updatedAt: DateTime, // Last update timestamp // Media Assets assets: Array<{ caption: string | null; // Asset caption duration: number; // Duration for video/audio exif: string; // EXIF data for images height: number; // Asset height mediaSubtypes: string[]; // Media subtypes mediaType: string; // Media type uri: string; // Asset URI/URL width: number; // Asset width createdAt: DateTime; // Asset creation timestamp updatedAt: DateTime; // Asset update timestamp }>, // Survey Responses surveyResponses: Array<{ fieldId: string; // Survey field ID response: string; // User's response createdAt: DateTime; // Response creation timestamp updatedAt: DateTime; // Response update timestamp }>, // Associated Claim claim: { id: string, // MongoDB ObjectId of the claim completed: boolean, // Whether claim is completed completedAt: DateTime | null, // When claim was completed submitted: boolean, // Whether claim is submitted submittedAt: DateTime | null, // When claim was submitted opportunity: { // Associated opportunity id: string, // MongoDB ObjectId of the opportunity name: string, // Opportunity name oneLineDescription: string, // Short description image: string | null, // Opportunity image URL } | null, } | null, };
A successful webhook call will receive a 2XX response. Any other response code indicates a failure.
The webhook implementation includes error handling:
try { const webhookResponse = await fetch(process.env.VERIFICATION_WEBHOOK_URL, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${process.env.VERIFICATION_WEBHOOK_SECRET}`, }, body: JSON.stringify(webhookData), }); if (!webhookResponse.ok) { console.error( "Verification webhook call failed:", await webhookResponse.text() ); } } catch (error) { console.error("Error calling verification webhook:", error); }
When a claim is completed and all verifications are approved, a webhook is sent to notify external systems about the successful claim completion.
The webhook endpoint and authentication are configured through environment variables:
WEBHOOK_URL: The URL where the webhook will be sentWEBHOOK_SECRET: The secret token used for webhook authenticationMethod: POST
Headers:
Content-Type: application/json Authorization: Bearer ${WEBHOOK_SECRET}
The webhook sends a JSON payload with the following structure:
const webhookData = { // Claim Information claimId: string, // MongoDB ObjectId of the claim verifications: TaskVerification[], // Array of task verifications completed: boolean, // Whether the claim is completed completedAt: DateTime | null, // When the claim was completed createdAt: DateTime, // When the claim was created purchaseAmount: number | null, // Purchase amount if applicable rejected: boolean, // Whether the claim was rejected submitted: boolean, // Whether the claim was submitted submittedAt: DateTime | null, // When the claim was submitted updatedAt: DateTime, // Last update timestamp userId: string, // MongoDB ObjectId of the user userEmail: string, // Email of the user who made the claim opportunityId: string | null, // MongoDB ObjectId of the opportunity opportunityLocationId: string | null, // MongoDB ObjectId of the opportunity location // Opportunity Information (null if no opportunity associated) opportunity: { id: string, // MongoDB ObjectId of the opportunity active: boolean | null, // Whether the opportunity is active availableAnywhere: boolean | null, // Whether it's available anywhere reward: Reward | null, // Associated reward details tasks: Task[], // Array of tasks boost: boolean | null, // Whether it's boosted budget: number | null, // Budget amount createdAt: DateTime, // Creation timestamp currency: string | null, // Currency code detailedDescription: string | null, // Detailed description draft: boolean | null, // Whether it's a draft endDate: DateTime, // End date gift: boolean | null, // Whether it's a gift image: string | null, // Image URL instanceLimit: number | null, // Instance limit locationDependent: boolean | null, // Whether it's location dependent locationTagIds: string[], // Array of location tag ObjectIds locationLimit: number | null, // Location limit name: string, // Opportunity name oneLineDescription: string, // Short description perpetual: boolean | null, // Whether it's perpetual startDate: string | null, // Start date targeted: boolean | null, // Whether it's targeted type: OpportunityType | null, // Opportunity type updatedAt: DateTime, // Last update timestamp video: string | null, // Video URL completed: boolean, // Whether it's completed viewCounter: number[], // View count array clickCounter: number[], // Click count array saveCounter: number[], // Save count array counterStartDate: DateTime | null, // Counter start date tags: string[] // Array of tag ObjectIds } | null }
A successful webhook call will receive a 2XX response. Any other response code indicates a failure.
The webhook implementation includes error handling:
try { const webhookResponse = await fetch(process.env.WEBHOOK_URL, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${process.env.WEBHOOK_SECRET}`, }, body: JSON.stringify(webhookData), }); if (!webhookResponse.ok) { console.error("Webhook call failed:", await webhookResponse.text()); } } catch (error) { console.error("Error calling webhook:", error); }
When a reward is generated after a successful claim completion, a webhook is sent to notify external systems about the reward details.
The webhook endpoint and authentication are configured through environment variables:
REWARD_WEBHOOK_URL: The URL where the reward webhook will be sentREWARD_WEBHOOK_SECRET: The secret token used for webhook authenticationMethod: POST
Headers:
Content-Type: application/json Authorization: Bearer ${REWARD_WEBHOOK_SECRET}
The webhook sends a JSON payload with the following structure:
const webhookData = { // Reward Information rewardId: string, // MongoDB ObjectId of the reward type: RewardType, // Type of reward (Voucher, CouponFixed, etc.) description: string | null, // Reward description restriction: string | null, // Any restrictions on the reward expiration: number, // Number of days until expiration // Voucher Information (always included) voucher: { amount: number | null, // Calculated voucher amount text: string | null, // Formatted voucher text (e.g., "$100") variable: boolean | null, // Whether amount varies with purchase low: number | null, // Minimum amount high: number | null, // Maximum amount voucherTiers: string[] | null, // Tier-specific voucher values purchaseAmount: number | null // Associated purchase amount if any }, // Coupon Information (always included) coupon: { id: string, // MongoDB ObjectId of the coupon amount: number | null, // Amount for fixed coupons couponType: CouponType | null, // Type of coupon (Fixed, Percentage, Custom) percentage: number | null, // Percentage for percentage coupons expires: Date, // Expiration date description: string | null, // Coupon description restriction: string | null, // Coupon restrictions image: string | null // Coupon image URL }, // User Information user: { id: string, // MongoDB ObjectId of the user email: string, // User's email address username: string | null, // User's username rank: Rank // User's rank (Bronze, Silver, etc.) }, // Claim Information claim: { id: string, // MongoDB ObjectId of the claim completed: boolean, // Whether claim is completed completedAt: Date, // When claim was completed createdAt: Date, // When claim was created submitted: boolean, // Whether claim was submitted submittedAt: Date | null, // When claim was submitted verifications: Array<{ // Array of verification statuses id: string, // Verification ID approved: boolean, // Whether verification was approved rejected: boolean, // Whether verification was rejected rejectedReason: string | null, // Reason for rejection if any submitted: boolean, // Whether verification was submitted submittedAt: Date | null // When verification was submitted }> }, // Opportunity Information opportunity: { id: string, // MongoDB ObjectId of the opportunity name: string, // Opportunity name oneLineDescription: string, // Short description detailedDescription: string | null, // Detailed description image: string | null // Opportunity image URL } }
A successful webhook call will receive a 2XX response. Any other response code indicates a failure.
The webhook implementation includes error handling:
try { const webhookResponse = await fetch(process.env.REWARD_WEBHOOK_URL, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${process.env.REWARD_WEBHOOK_SECRET}`, }, body: JSON.stringify(webhookData), }); if (!webhookResponse.ok) { console.error("Reward webhook call failed:", await webhookResponse.text()); } } catch (error) { console.error("Error calling reward webhook:", error); }
When an external coupon is redeemed by a user, a webhook is sent to the configured external system to handle the redemption process.
The webhook endpoint is configured at the shop level via the
Shop.webhookUrls array in the database. The system will select the first URL in this list that matches a coupon-specific pattern (e.g., contains "coupon"). The authentication is configured through environment variables:
EXTERNAL_COUPON_WEBHOOK_SECRET: The secret token used for webhook authenticationMethod: POST
Headers:
Content-Type: application/json Authorization: Bearer ${EXTERNAL_COUPON_WEBHOOK_SECRET}
The webhook sends a JSON payload with the following structure:
const webhookPayload = { // Coupon Information couponId: string, // MongoDB ObjectId of the coupon being redeemed userId: string, // MongoDB ObjectId of the user redeeming the coupon // User Information user: { id: string, // MongoDB ObjectId of the user username: string, // User's username email: string, // User's email address }, // Coupon Details coupon: { id: string, // MongoDB ObjectId of the coupon couponType: CouponType, // Type of coupon (Fixed, Percentage, Custom) amount: number | null, // Amount for fixed coupons (in cents) percentage: number | null, // Percentage for percentage coupons description: string | null, // Coupon description expires: Date, // Expiration date restriction: string | null, // Coupon restrictions }, // Metadata timestamp: string, // ISO timestamp of redemption };
The external webhook endpoint should return a JSON response with the following structure:
{ success: boolean, // Whether the redemption was successful message?: string, // Optional message to display to the user instructions?: string, // Optional instructions for the user externalId?: string, // Optional external reference ID data?: any // Optional additional data }
// Example webhook endpoint implementation app.post("/webhook/coupon-redemption", (req, res) => { const { couponId, userId, user, coupon, timestamp } = req.body; // Validate the Bearer token const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith("Bearer ")) { return res.status(401).json({ error: "Unauthorized" }); } const token = authHeader.substring(7); if (token !== process.env.EXTERNAL_COUPON_WEBHOOK_SECRET) { return res.status(401).json({ error: "Invalid token" }); } try { // Process the coupon redemption in your external system const result = await processExternalCouponRedemption({ couponId, userId, user, coupon, timestamp, }); // Return success response res.json({ success: true, message: "Coupon redeemed successfully!", instructions: "Present this confirmation at checkout.", externalId: result.id, }); } catch (error) { console.error("Error processing coupon redemption:", error); res.status(500).json({ success: false, error: "Failed to process coupon redemption", }); } });
When an external voucher is redeemed by a user, a webhook is sent to the configured external system to handle the redemption process.
The webhook endpoint is configured at the shop level via the
Shop.webhookUrls array in the database. The system will select the first URL in this list that matches a voucher-specific pattern (e.g., contains "voucher"). The authentication is configured through environment variables:
EXTERNAL_VOUCHER_WEBHOOK_SECRET: The secret token used for webhook authenticationMethod: POST
Headers:
Content-Type: application/json Authorization: Bearer ${EXTERNAL_VOUCHER_WEBHOOK_SECRET}
The webhook sends a JSON payload with the following structure:
const webhookPayload = { // Voucher Information voucherId: string, // MongoDB ObjectId of the voucher being redeemed userId: string, // MongoDB ObjectId of the user redeeming the voucher // User Information user: { id: string, // MongoDB ObjectId of the user username: string, // User's username email: string, // User's email address }, // Voucher Details voucher: { id: string, // MongoDB ObjectId of the voucher text: string, // Voucher text/title amount: number | null, // Amount for value vouchers (in cents) description: string | null, // Voucher description expires: Date, // Expiration date restriction: string | null, // Voucher restrictions }, // Metadata timestamp: string, // ISO timestamp of redemption };
The external webhook endpoint should return a JSON response with the following structure:
{ success: boolean, // Whether the redemption was successful message?: string, // Optional message to display to the user instructions?: string, // Optional instructions for the user externalId?: string, // Optional external reference ID data?: any // Optional additional data }
// Example webhook endpoint implementation app.post("/webhook/voucher-redemption", (req, res) => { const { voucherId, userId, user, voucher, timestamp } = req.body; // Validate the Bearer token const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith("Bearer ")) { return res.status(401).json({ error: "Unauthorized" }); } const token = authHeader.substring(7); if (token !== process.env.EXTERNAL_VOUCHER_WEBHOOK_SECRET) { return res.status(401).json({ error: "Invalid token" }); } try { // Process the voucher redemption in your external system const result = await processExternalVoucherRedemption({ voucherId, userId, user, voucher, timestamp, }); // Return success response res.json({ success: true, message: "Voucher redeemed successfully!", instructions: "Present this confirmation at the store.", externalId: result.id, }); } catch (error) { console.error("Error processing voucher redemption:", error); res.status(500).json({ success: false, error: "Failed to process voucher redemption", }); } });
Contact Legion Hand Technologies support for assistance with widget integration.