
Ugc Campaign Management
- 63 installs
- 41 repo stars
- Updated March 13, 2026
- finsilabs/awesome-ecommerce-skills
Sources, curates, and displays user-generated content at scale with rights management, brand-safety moderation, and social-proof galleries.
About
Manages UGC campaigns end to end with content sourcing, rights management, moderation, and trust-building galleries. A developer uses it to scale social proof for an ecommerce brand.
- Rights management and brand-safety moderation
- Social-proof galleries for trust and conversion
Ugc Campaign Management by the numbers
- 63 all-time installs (skills.sh)
- Ranked #531 of 853 Sales & Marketing skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill ugc-campaign-managementAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 63 |
|---|---|
| repo stars | ★ 41 |
| Last updated | March 13, 2026 |
| Repository | finsilabs/awesome-ecommerce-skills ↗ |
What it does
Sources, curates, and displays user-generated content at scale with rights management, brand-safety moderation, and social-proof galleries.
Files
UGC Campaign Management
Overview
User-generated content — photos, videos, and reviews from real customers — consistently outperforms brand-created content. Shoppers are 79% more likely to purchase when they see UGC, and UGC ads on paid social typically achieve 4× higher CTR than studio-produced ads. Dedicated UGC platforms (Yotpo, Okendo, TINT) handle collection, rights management, moderation, and display widgets. Custom pipelines only make sense for headless stores with high UGC volume.
When to Use This Skill
- When product pages need social proof beyond star ratings
- When paid social creative is stale and UGC-style ads would boost performance
- When launching a hashtag campaign to generate organic content at scale
- When needing a rights management workflow before using customer photos in ads
- When building a shoppable Instagram or TikTok gallery on your storefront
Core Instructions
Step 1: Choose the right UGC platform
| Platform | Best For | Shopify | WooCommerce | BigCommerce | Price |
|---|---|---|---|---|---|
| Yotpo | Reviews + photos + social syncing | App Store | Plugin | App Marketplace | Free tier; $119+/mo |
| Okendo | Photo/video reviews, attribute ratings | App Store | — | — | $19+/mo |
| TINT | Shoppable galleries, enterprise | Via JS | Via JS | Via JS | $500+/mo |
| Loox | Photo-focused reviews, Shopify-native | App Store | — | — | $9+/mo |
| Stackla | Hashtag monitoring + brand gallery | Via JS | Via JS | Via JS | Custom pricing |
Recommendation: Use Loox or Okendo for Shopify if your priority is photo review collection on product pages. Use Yotpo if you want both review collection and shoppable UGC galleries. For WooCommerce, Yotpo or a combination of WooCommerce product reviews (built-in) + a gallery plugin covers most needs.
Step 2: Set up UGC collection
---
Shopify with Loox (photo reviews)
1. Install Loox from the Shopify App Store 2. Go to Loox → Settings → Review Request Emails:
- Enable automatic review requests after order fulfillment
- Set timing: 14 days after order delivered
- Enable Photo review incentive: "Add a photo to your review and get 10% off your next order"
3. Go to Loox → Widgets and add the photo review carousel to product pages and the homepage 4. Loox automatically moderates submitted photos and issues the discount code after approval
---
Shopify with Yotpo (photo reviews + social syncing)
1. Install Yotpo from the Shopify App Store 2. Go to Yotpo → Reviews → Mail After Purchase and enable photo review requests at 14 days post-delivery 3. Go to Yotpo → Gallery → Sources to connect Instagram:
- Add your brand's Instagram hashtag
- Yotpo pulls all public posts with your hashtag automatically
4. Go to Yotpo → Gallery → Moderation to review and approve imported Instagram UGC 5. For each Instagram post you want to feature: click Request Rights — Yotpo sends an automated comment requesting usage permission 6. Go to Yotpo → Gallery → Display to add a shoppable UGC gallery section to your product pages or homepage
---
WooCommerce
1. Enable WooCommerce's built-in product reviews under WooCommerce → Settings → Products → Reviews 2. For photo reviews: install Customer Reviews for WooCommerce plugin (free tier available) which adds photo upload to the review form 3. For social UGC galleries: install Flow-Flow Social Stream plugin to pull Instagram/TikTok content by hashtag and display on-site 4. For rights management on collected UGC: use TINT or Stackla via their JavaScript embed — both work with any platform
---
BigCommerce
1. Install Yotpo from the BigCommerce App Marketplace 2. Configuration mirrors the Shopify Yotpo setup above 3. For additional hashtag-sourced UGC: connect Instagram via Yotpo's Social Feeds feature
---
Custom / Headless
For headless stores, build the UGC collection via Instagram API and a direct upload widget:
// Fetch posts by hashtag from Instagram Graph API
async function fetchHashtagPosts(hashtag: string, since: Date) {
const hashtagSearch = await fetch(
`https://graph.facebook.com/v18.0/ig_hashtag_search?user_id=${process.env.IG_USER_ID}&q=${hashtag}&access_token=${process.env.IG_ACCESS_TOKEN}`
);
const { id: hashtagId } = await hashtagSearch.json();
const postsResponse = await fetch(
`https://graph.facebook.com/v18.0/${hashtagId}/recent_media?user_id=${process.env.IG_USER_ID}&fields=id,media_url,permalink,caption,timestamp,username&access_token=${process.env.IG_ACCESS_TOKEN}`
);
const { data: posts } = await postsResponse.json();
for (const post of posts.filter(p => new Date(p.timestamp) > since)) {
await db.ugcSubmissions.upsert({ externalId: post.id }, {
source: 'hashtag',
platform: 'instagram',
authorHandle: post.username,
mediaUrl: post.media_url,
contentUrl: post.permalink,
caption: post.caption,
status: 'pending', // always start as pending — requires moderation + rights
});
}
}Step 3: Rights management
Never use customer content in ads or on your website without explicit permission. Instagram terms prohibit commercial use of UGC without rights. This is not optional.
In Yotpo (automated): 1. When you approve Instagram UGC in the Yotpo gallery, click Request Rights 2. Yotpo automatically posts a comment on the original Instagram photo asking the creator to reply "Yes" to grant rights 3. When the creator replies, Yotpo marks the content as rights-approved 4. Only rights-approved content appears in your published gallery
For direct upload via your review form (Judge.me, Loox, Okendo):
- Photo reviews submitted through your store's review form are covered by the review platform's terms of service, which include usage rights for the store
- No separate rights request needed for review photos
Manual rights request (if not using a UGC platform): Send a comment: "We love this photo! May we feature it on our website and in our marketing? Reply 'YES' to grant permission or visit [link] to manage preferences."
Step 4: Moderate UGC before publishing
Automated moderation via Yotpo/Loox:
- Both platforms include automatic content safety screening
- Set up keyword filters under the moderation settings to catch competitor mentions, inappropriate language, or misleading claims
Manual moderation checklist before approving any UGC: 1. Product is clearly visible and matches the product listing 2. No competitor products or branding visible 3. No inappropriate content (adult, violence) 4. No misleading product claims 5. Rights have been granted (for Instagram UGC)
Step 5: Display UGC on product pages
In Yotpo: go to Gallery → Display → Add to Store — drag the UGC gallery widget into your product page template in the Shopify Theme Editor.
In Loox: go to Widgets → Photo Reviews and add to product page via Shopify Theme Editor.
Key placements (in order of conversion impact): 1. Product page — below the description, before customer reviews 2. Homepage — "As worn by our customers" gallery section 3. Cart page — "Customers who bought this also shared" section 4. Post-purchase confirmation page — social sharing prompt showing similar UGC
Step 6: Use UGC in paid social ads
UGC-style ads consistently outperform polished brand content on TikTok and Meta. Steps to repurpose approved UGC:
1. Download the original high-resolution media from your UGC platform (rights must be granted) 2. In Meta Ads Manager: create a new ad and upload the UGC image or video 3. For Spark Ads on TikTok: use the creator's original TikTok post (not a download) via Spark Ad authorization 4. Track performance — compare UGC ads vs. brand ads using the same targeting and budget; measure CTR and conversion rate
Best Practices
- Build UGC collection into your post-purchase flow permanently — the post-purchase review request email is the most reliable ongoing source of UGC; keep it running indefinitely
- Respond publicly to tagged posts — commenting "Love this! We featured it on our website" on customer posts increases future submissions organically
- Feature UGC in your email campaigns — adding a customer photo to a post-purchase flow email increases click-through rate significantly compared to product images
- Rotate UGC in paid ads every 2–4 weeks — creative fatigue is faster in social advertising; a steady UGC pipeline is a competitive advantage
- Offer non-monetary recognition — being featured on the brand's official channel is often more motivating than a discount; create a "Featured Customer" Instagram highlight
Common Pitfalls
| Problem | Solution |
|---|---|
| Using content without rights and receiving a DMCA complaint | Never publish UGC until rights status is "approved" in your platform; review photos from your form are covered, but Instagram hashtag posts require separate rights |
| Low response rate to rights requests | Simplify: "Reply YES to grant permission" in a comment performs 3× better than linking to a form |
| UGC gallery loading slowly | Pre-generate thumbnails and serve via CDN; lazy-load images below the fold with IntersectionObserver |
| Competitor products appearing in approved UGC | Add competitor brand name detection to your moderation checklist; reject or crop images with competitor branding |
| UGC volume drops after initial campaign | The post-purchase review request must run permanently — it is the only reliable ongoing source |
Related Skills
- @review-generation-engine
- @social-proof-widgets
- @influencer-marketplace-integration
- @tiktok-ads-integration
- @content-commerce
{
"context": "Tests whether the agent implements a complete UGC content moderation pipeline with the correct APIs, thresholds, flag names, moderation score formula, routing logic, and human review queue.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Vision SafeSearch API",
"max_score": 8,
"description": "Code calls a Vision Safe Search API (callVisionSafeSearchAPI or equivalent) to check image content rather than a different image analysis service"
},
{
"name": "adult-content flag",
"max_score": 10,
"description": "Flags submission as 'adult-content' when adult result is 'LIKELY' or 'VERY_LIKELY' (both thresholds, not just one)"
},
{
"name": "violence flag",
"max_score": 8,
"description": "Flags submission as 'violence' when violence result is 'LIKELY'"
},
{
"name": "Perspective API",
"max_score": 8,
"description": "Uses Perspective API (callPerspectiveAPI or equivalent) to check caption toxicity"
},
{
"name": "Toxicity threshold 0.8",
"max_score": 10,
"description": "Flags caption as 'toxic-language' only when toxicity score is greater than 0.8 (not a different threshold)"
},
{
"name": "Competitor detection",
"max_score": 8,
"description": "Reads competitor brands from COMPETITOR_BRANDS environment variable (comma-separated) and flags captions containing any competitor name as 'competitor-mention'"
},
{
"name": "Moderation score formula",
"max_score": 8,
"description": "Calculates moderation score as flags.length divided by 5 (not a different normalization formula)"
},
{
"name": "Zero-flag approval only",
"max_score": 8,
"description": "Auto-approves submissions ONLY when the flag count is 0 (not when score is below some other threshold)"
},
{
"name": "Hard reject for adult/violence",
"max_score": 10,
"description": "Submissions with 'adult-content' OR 'violence' flags are set to status 'rejected' and NOT sent to human review"
},
{
"name": "Human review queue",
"max_score": 10,
"description": "Submissions with non-zero flags that are not adult/violence are added to a human moderation queue (not auto-rejected or silently dropped)"
},
{
"name": "Rights request after approval",
"max_score": 12,
"description": "When a submission is auto-approved (zero flags), the code triggers a rights request for that submission"
}
]
}
UGC Brand Safety Moderation System
Problem/Feature Description
Zephyr Outdoor is a mid-sized sporting goods brand that has launched a major hashtag campaign across Instagram and TikTok. Within the first week, thousands of customer posts have come in — but the marketing team quickly realized they cannot manually review everything. They need an automated pipeline that screens incoming content for brand safety before it can be published anywhere.
The moderation system needs to catch explicit or violent imagery, screen text for harmful language, and identify posts where competitor products are prominently mentioned or shown. At the same time, the team knows that AI moderation is imperfect — especially for edge cases involving sporting activities that may look violent, or captions with sarcasm. The solution must route ambiguous content to a human review queue rather than making all decisions automatically. The system should integrate with external content analysis services for image and text screening.
Output Specification
Write a TypeScript module implementing the content moderation pipeline as moderation.ts. The module should:
- Implement a function that takes a UGC submission and returns a moderation result with flags and a score
- Implement a queue-processing function that processes pending submissions and routes them to the correct outcome (approval, rejection, or human review)
Also provide a brief moderation-notes.md explaining what each flag means and when human review is triggered vs. automatic rejection.
Input Files
The following types are provided. Extract them before beginning.
=============== FILE: types.ts =============== export interface UGCSubmission { id: string; externalId: string; source: string; platform: string; authorHandle: string; authorEmail?: string; contentUrl: string; mediaUrls: string[]; caption?: string; productIds: string[]; submittedAt: Date; status: 'pending' | 'approved' | 'rejected' | 'rights-requested' | 'rights-granted'; moderationFlags?: string[]; thumbnailUrl?: string; engagementScore?: number; }
export interface ModerationResult { approved: boolean; flags: string[]; score: number; }
// Stubs — implement the logic that calls these export declare function callVisionSafeSearchAPI(mediaUrl: string): Promise<{ adult: string; violence: string; racy: string; }>;
export declare function callPerspectiveAPI(text: string): Promise<number>;
export declare const db: { ugcSubmissions: { findAll(options: object): Promise<UGCSubmission[]>; update(id: string, data: Partial<UGCSubmission>): Promise<void>; }; ugcModerationQueue: { create(data: { submissionId: string; flags: string[] }): Promise<void>; }; };
export declare function requestContentRights(submissionId: string): Promise<void>;
{
"context": "Tests whether the agent implements a correct UGC rights management system including token generation, rights request messaging, status transitions, scope definition, and incentivized thank-you flow when rights are granted.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Status: rights-requested",
"max_score": 10,
"description": "Submission status is updated to 'rights-requested' (not just a generic pending/contacted state) after the rights request is sent"
},
{
"name": "Secure token generated",
"max_score": 8,
"description": "A unique token (rightsToken or equivalent) is generated and stored with the submission to enable the rights grant link"
},
{
"name": "Reply YES simplicity",
"max_score": 10,
"description": "The rights request message instructs the user to reply with a single word (e.g. 'YES') rather than directing them only to a form or complex process"
},
{
"name": "#ugcpermission hashtag",
"max_score": 8,
"description": "The rights request comment/DM includes the '#ugcpermission' hashtag"
},
{
"name": "Manage preferences link",
"max_score": 8,
"description": "The rights request message includes a link where the user can manage their preferences (a rights grant URL with the token)"
},
{
"name": "Status: rights-granted",
"max_score": 10,
"description": "Submission status is updated to 'rights-granted' (exact value) when the user approves"
},
{
"name": "Rights scope definition",
"max_score": 10,
"description": "Rights scope is recorded as covering website, ads, and email (all three) upon grant"
},
{
"name": "Thank-you discount code",
"max_score": 8,
"description": "A 10% discount code is sent to the author via email as a thank-you after rights are granted"
},
{
"name": "No publish without rights",
"max_score": 10,
"description": "The code that retrieves or displays UGC filters by status === 'rights-granted' and does NOT return/render content with any other status"
},
{
"name": "rightsRequestedAt timestamp",
"max_score": 8,
"description": "A timestamp (rightsRequestedAt or equivalent) is recorded when the rights request is sent"
},
{
"name": "rightsGrantedAt timestamp",
"max_score": 10,
"description": "A timestamp (rightsGrantedAt or equivalent) is recorded when rights are granted"
}
]
}
UGC Rights Management System
Problem/Feature Description
A fashion accessories brand called Lumara has been running a successful hashtag campaign on Instagram for six months. Their community has tagged hundreds of posts featuring Lumara products, and the marketing team wants to start using these customer photos on product pages and in paid social ads. However, the legal team has flagged that using customer-created content — even when customers voluntarily tag the brand — requires explicit permission before any commercial use.
The engineering team needs to build the rights management module that sits between content discovery and publication. The system must request permission from content creators, track whether permission has been granted, and prevent any customer content from appearing on the site or in ad creatives until the creator has formally approved its use. The module also needs to reward creators who grant rights, as this has been shown to increase the overall rights-grant rate.
Output Specification
Write a TypeScript module that implements the rights management workflow. The module should include:
- A function to send a rights request to a content creator (via Instagram comment)
- An HTTP endpoint handler for the rights grant page (where creators click to approve)
- A function or query example showing how to safely retrieve only content that is cleared for use
Save the implementation as rights-management.ts.
Provide a brief notes.md explaining the key design decisions in your implementation.
Input Files
The following skeleton types file is provided to give you the database interface shape. Extend it as needed. Extract it before beginning.
=============== FILE: types.ts =============== export interface UGCSubmission { id: string; externalId: string; source: string; platform: string; authorHandle: string; authorEmail?: string; contentUrl: string; mediaUrls: string[]; caption?: string; productIds: string[]; submittedAt: Date; status: string; // Add any additional fields your rights management system requires [key: string]: unknown; }
// Database interface (stubbed — implement the logic, not the db layer) export declare const db: { ugcSubmissions: { findById(id: string): Promise<UGCSubmission | null>; findByToken(token: string): Promise<UGCSubmission | null>; update(id: string, data: Partial<Record<string, unknown>>): Promise<void>; findAll(options: object): Promise<UGCSubmission[]>; }; };
export declare function postInstagramComment(externalId: string, message: string): Promise<void>; export declare function sendEmail(email: string, template: string, payload: object): Promise<void>; export declare function createUniqueDiscount(options: { type: string; value: number }): Promise<string>; export declare function generateSecureToken(): string;
{
"context": "Tests whether the agent correctly implements the UGC collection pipeline (three channels, data model, post-purchase email timing) and the product page display API (filtering, ordering, response shape, pagination defaults).",
"type": "weighted_checklist",
"checklist": [
{
"name": "Three collection channels",
"max_score": 8,
"description": "Implementation references or supports all three collection channels: post-purchase email requests, hashtag monitoring, and direct upload widget (not just one or two)"
},
{
"name": "UGCSubmission source values",
"max_score": 8,
"description": "The source field type includes all four values: 'hashtag', 'email-request', 'widget', 'review-photo'"
},
{
"name": "UGCSubmission status values",
"max_score": 8,
"description": "The status field type includes all five values: 'pending', 'approved', 'rejected', 'rights-requested', 'rights-granted'"
},
{
"name": "14-day email delay",
"max_score": 10,
"description": "Post-purchase UGC request email is scheduled with a 14-day delay after delivery (not a different delay period)"
},
{
"name": "Email incentive text",
"max_score": 8,
"description": "Post-purchase email payload includes an incentive referencing 10% off for approved posts"
},
{
"name": "Duplicate ingest prevention",
"max_score": 8,
"description": "Hashtag ingestion checks whether a post with the same external ID already exists before creating a new submission record"
},
{
"name": "Display: rights-granted only",
"max_score": 10,
"description": "The product UGC display API filters submissions to only those with status 'rights-granted'"
},
{
"name": "Display: engagementScore order",
"max_score": 8,
"description": "The display API orders results by engagementScore descending"
},
{
"name": "Display: default limit=12",
"max_score": 8,
"description": "The display API uses a default limit of 12 items per page (not another number)"
},
{
"name": "Display: thumbnailUrl in response",
"max_score": 8,
"description": "The display API response includes thumbnailUrl for each item"
},
{
"name": "Display: contentUrl in response",
"max_score": 8,
"description": "The display API response includes contentUrl (the original post URL) for each item"
},
{
"name": "Display: total count included",
"max_score": 8,
"description": "The display API response includes a total count of rights-granted submissions for that product (not just the paginated items)"
}
]
}
UGC Collection Pipeline and Product Gallery API
Problem/Feature Description
Bloom & Co is a home fragrance brand launching its first structured UGC program after noticing that customers are spontaneously sharing beautiful photos of their candles and diffusers online. The brand wants to systematically collect this content from multiple touchpoints — customers who just placed an order, people posting with the brand hashtag on Instagram, and visitors who want to upload content directly on the website.
Once collected, the content needs to surface on individual product pages where it has been shown to improve purchase confidence. The frontend team is ready to build the gallery component and needs a backend API endpoint they can call to fetch approved customer photos for any given product. The API should support pagination for products with large amounts of content and return everything the gallery component needs to render a tile — including the original post link so shoppers can click through to see the real customer context.
Output Specification
Write the following TypeScript files:
1. ugc-types.ts — The TypeScript interface(s) for the UGC submission data model 2. ugc-collection.ts — Functions implementing the collection pipeline:
- A function to send a post-purchase UGC request email
- A function to ingest posts from Instagram hashtag monitoring into the database
3. ugc-display.ts — The Express route handler for the product UGC display API endpoint
Also provide design-notes.md with a brief description of how the collection channels work and any important decisions about the data model or API design.
Input Files
The following Express and database stubs are provided. Extract them before beginning.
=============== FILE: stubs.ts =============== import { Request, Response } from 'express';
export { Request, Response };
// Schedule an email to be sent after a delay export declare function scheduleEmail( customerId: string, template: string, options: { subject: string; delay: number; payload: Record<string, unknown>; } ): Promise<void>;
// Stub DB — implement logic that calls these export declare const db: { ugcSubmissions: { findByExternalId(externalId: string): Promise<unknown | null>; create(data: Record<string, unknown>): Promise<{ id: string }>; findAll(options: object): Promise<unknown[]>; count(options: object): Promise<number>; }; };
export interface Order { id: string; customerId: string; lineItems: Array<{ productId: string; name: string }>; }
{
"name": "finsi/ugc-campaign-management",
"version": "0.1.0",
"summary": "Source, curate, and display user-generated content at scale with rights management, brand safety moderation, and trust-building social proof galleries",
"skills": {
"ugc-campaign-management": {
"path": "SKILL.md"
}
}
}