Identity Check Integration Guide
Identity Check Integration Guide
Section titled “Identity Check Integration Guide”This guide walks you through integrating LMIF identity checks into your avatar creation flow.
Overview
Section titled “Overview”The identity check should happen:
- Before avatar creation - Check when users submit avatar details
- On existing content - Scan your existing avatars periodically
- On webhook events - Re-check when new identities are boxed
Step-by-Step Integration
Section titled “Step-by-Step Integration”-
Add the check to your avatar creation flow
Insert the identity check before saving the avatar to your database:
// Beforeasync function createAvatar(data: AvatarData) {const avatar = await db.avatars.create(data);return avatar;}// Afterasync function createAvatar(data: AvatarData) {// Check identity firstconst check = await lmif.identity.check({name: data.name,imageUrl: data.imageUrl,description: data.description,});// Handle the resultif (check.action === 'BLOCK') {throw new AvatarBlockedError('This identity is protected and cannot be used',check.matchedIdentity);}// Store LMIF data with avatarconst avatar = await db.avatars.create({...data,lmif: {boxId: check.matchedIdentity?.boxId,policy: check.policy,royaltyRate: check.policyDetails?.royaltyRate,checkedAt: new Date(),},});return avatar;} -
Handle all policy types
Create handlers for each policy type:
async function handleIdentityCheck(check: IdentityCheckResult) {switch (check.action) {case 'ALLOW':return { proceed: true };case 'BLOCK':return {proceed: false,error: 'This identity is protected',suggestion: 'Try creating an original character instead',};case 'REQUIRE_LICENSE':return {proceed: false,requiresLicense: true,licenseUrl: `/license/${check.matchedIdentity.boxId}`,licenseTiers: await getLicenseTiers(check.matchedIdentity.boxId),};case 'TRACK_REVENUE':return {proceed: true,tracking: {enabled: true,boxId: check.matchedIdentity.boxId,royaltyRate: check.policyDetails.royaltyRate,},};case 'VERIFY_COMMERCIAL':return {proceed: 'pending',question: 'Will this avatar be used for commercial purposes?',onYes: { action: 'BLOCK' },onNo: { action: 'ALLOW', tracking: 'non_commercial' },};case 'REVIEW_PARODY':return {proceed: 'pending',question: 'Is this a parody or satirical avatar?',requirements: 'Parody avatars must include a clear disclaimer',};default:return { proceed: false, error: 'Unknown policy' };}} -
Build the UI for blocked identities
When an identity is blocked, show a helpful message:
function AvatarBlockedDialog({ identity }: { identity: MatchedIdentity }) {return (<Dialog><DialogTitle>Identity Protected</DialogTitle><DialogContent><p><strong>{identity.name}</strong> has protected their identity.AI avatars using this likeness are not allowed.</p><p><Link href="/create/original">Create an original character instead</Link></p></DialogContent></Dialog>);} -
Build the licensing flow
For
LICENSEpolicy, guide users through obtaining a license:function LicenseRequired({ boxId }: { boxId: string }) {const [tiers, setTiers] = useState<LicenseTier[]>([]);useEffect(() => {loadLicenseTiers(boxId).then(setTiers);}, [boxId]);return (<Dialog><DialogTitle>License Required</DialogTitle><DialogContent><p>This identity requires a license. Choose a tier:</p>{tiers.map((tier) => (<LicenseTierCardkey={tier.name}tier={tier}onSelect={() => requestLicense(boxId, tier.name)}/>))}</DialogContent></Dialog>);} -
Track revenue for MONETIZE policy
When
TRACK_REVENUEis returned, enable usage tracking:class AvatarUsageTracker {async trackInteraction(avatarId: string, type: 'message' | 'tip' | 'subscription') {const avatar = await db.avatars.get(avatarId);if (!avatar.lmif?.boxId) return;// Store locally for batch reportingawait db.usageMetrics.increment({avatarId,boxId: avatar.lmif.boxId,type,date: new Date().toISOString().split('T')[0],});}async reportMonthlyUsage() {const metrics = await db.usageMetrics.getByMonth(getCurrentMonth());for (const avatarMetrics of metrics) {const license = await lmif.licenses.getByBox(avatarMetrics.boxId);await lmif.licenses.reportUsage(license.id, {period: getCurrentMonth(),metrics: avatarMetrics.counts,revenue: await calculateRevenue(avatarMetrics),});}}}
Handling Edge Cases
Section titled “Handling Edge Cases”Common Names
Section titled “Common Names”The system requires both image AND name to match. “John Smith” alone won’t trigger a match:
// This won't match a protected identityconst check = await lmif.identity.check({ name: 'John Smith', // Common name imageUrl: 'https://example.com/generic-person.jpg', // Generic image});// check.isBoxed === false (probably)
// This WILL match if John Smith the celebrity is protectedconst check2 = await lmif.identity.check({ name: 'John Smith', imageUrl: 'https://example.com/john-smith-celebrity.jpg', // Actual likeness});// check2.isBoxed === true (if protected)Parody Content
Section titled “Parody Content”Handle parody detection appropriately:
if (check.action === 'REVIEW_PARODY') { // Ask user if this is parody const isParody = await promptUser('Is this a parody avatar?');
if (isParody) { // Check if policy allows parody if (check.policyDetails?.allowedUses?.includes('parody')) { // Create with parody disclaimer await createAvatar({ ...data, isParody: true, disclaimer: 'This is a parody account and is not affiliated with the real person.', }); } else { throw new Error('This identity does not allow parody content'); } }}API Errors
Section titled “API Errors”Handle API errors gracefully:
async function safeIdentityCheck(name: string, imageUrl: string) { try { return await lmif.identity.check({ name, imageUrl }); } catch (error) { if (error.code === 'RATE_LIMITED') { // Implement queuing or retry await addToCheckQueue({ name, imageUrl }); return { action: 'PENDING', message: 'Check queued' }; }
// For other errors, decide on fail-open or fail-closed console.error('Identity check failed:', error);
// Fail-closed (safer) - block if we can't check return { action: 'BLOCK', reason: 'Unable to verify identity' };
// Fail-open (riskier) - allow if we can't check // return { action: 'ALLOW', warning: 'Unable to verify identity' }; }}Scanning Existing Content
Section titled “Scanning Existing Content”When you first integrate LMIF, scan your existing avatars:
async function scanExistingAvatars() { const avatars = await db.avatars.findAll({ lmif: null });
// Use batch API for efficiency const batches = chunk(avatars, 100);
for (const batch of batches) { const results = await lmif.identity.checkBatch( batch.map((a) => ({ id: a.id, name: a.name, imageUrl: a.imageUrl, })) );
for (const result of results) { await db.avatars.update(result.id, { lmif: { boxId: result.matchedIdentity?.boxId, policy: result.policy, checkedAt: new Date(), }, });
// Handle violations if (result.isBoxed && result.action === 'BLOCK') { await handleExistingViolation(result.id, result); } } }}Testing
Section titled “Testing”Test your integration with sandbox identities:
describe('Identity Check Integration', () => { it('blocks protected identities', async () => { const result = await createAvatar({ name: 'Test Celebrity A', // Sandbox: Always blocked imageUrl: 'https://example.com/test.jpg', });
expect(result.error).toBe('This identity is protected'); });
it('tracks revenue for monetized identities', async () => { const result = await createAvatar({ name: 'Test Celebrity B', // Sandbox: Monetized imageUrl: 'https://example.com/test.jpg', });
expect(result.tracking.enabled).toBe(true); expect(result.tracking.royaltyRate).toBe(0.10); });
it('handles rate limits gracefully', async () => { // Trigger rate limit in sandbox for (let i = 0; i < 100; i++) { await lmif.identity.check({ name: 'Test', imageUrl: 'https://...' }); }
// Should handle gracefully const result = await createAvatar({ name: 'Test', imageUrl: '...' }); expect(result.action).toBe('PENDING'); });});