Skip to content

Handling Boxing Events

This guide covers how to respond when identities are boxed, including grace period management and user notifications.

When a creator boxes their identity, your platform receives webhooks and may have existing avatars affected. Here’s how to handle this:

First, subscribe to relevant events:

await lmif.webhooks.create({
url: 'https://yourplatform.com/webhooks/lmif',
events: [
'box.created', // New identity boxed
'box.updated', // Policy changed
'violation.detected', // Match found on your platform
'grace_period.started', // 30-day countdown begins
'grace_period.reminder', // Day 7, 21, 28 reminders
'grace_period.ending', // 2 days left
'grace_period.expired', // Time to enforce
],
});

When a new identity is boxed, scan your existing avatars:

async function handleBoxCreated(event: BoxCreatedEvent) {
const { boxId, identityName, policy } = event.data;
// Search your avatars for potential matches
const potentialMatches = await db.avatars.search({
name: { $similar: identityName },
});
if (potentialMatches.length > 0) {
// Re-check each potential match
const checks = await lmif.identity.checkBatch(
potentialMatches.map((avatar) => ({
id: avatar.id,
name: avatar.name,
imageUrl: avatar.imageUrl,
}))
);
// Handle matches
for (const check of checks) {
if (check.isBoxed && check.matchedIdentity?.boxId === boxId) {
await handleNewViolation(check.id, check);
}
}
}
}

When a grace period starts, notify affected users:

async function handleGracePeriodStarted(event: GracePeriodStartedEvent) {
const { gracePeriodId, boxId, identityName, expiresAt, affectedAvatars } = event.data;
for (const avatar of affectedAvatars) {
// Notify avatar creator
await notifications.send({
userId: avatar.creatorId,
type: 'grace_period_started',
title: 'Action Required: Avatar Compliance',
message: `Your avatar "${avatar.name}" uses the likeness of ${identityName}, who has protected their identity. You have 30 days to comply.`,
data: {
gracePeriodId,
avatarId: avatar.avatarId,
expiresAt,
options: ['license', 'modify', 'remove'],
},
});
// Update avatar status in database
await db.avatars.update(avatar.avatarId, {
status: 'grace_period',
gracePeriod: {
id: gracePeriodId,
expiresAt,
identityName,
},
});
// Optionally notify users of the avatar
await notifyAvatarUsers(avatar.avatarId, {
type: 'avatar_warning',
message: `This avatar may become unavailable in 30 days. We'll suggest alternatives.`,
});
}
}

Send reminders at key intervals:

async function handleGracePeriodReminder(event: GracePeriodReminderEvent) {
const { gracePeriodId, reminderDay, daysRemaining, affectedAvatars } = event.data;
const urgency = {
7: 'normal',
21: 'elevated',
28: 'urgent',
}[reminderDay];
for (const avatar of affectedAvatars) {
// Skip already-resolved avatars
if (avatar.status === 'resolved') continue;
await notifications.send({
userId: avatar.creatorId,
type: 'grace_period_reminder',
urgency,
title: `${daysRemaining} days remaining`,
message: `Your avatar "${avatar.name}" needs attention. ${daysRemaining} days left to comply.`,
actions: [
{ label: 'Get License', url: `/license/${avatar.avatarId}` },
{ label: 'Modify Avatar', url: `/edit/${avatar.avatarId}` },
{ label: 'Remove Avatar', action: 'remove' },
],
});
}
}

Final warning before enforcement:

async function handleGracePeriodEnding(event: GracePeriodEndingEvent) {
const { gracePeriodId, expiresAt, unresolvedAvatars } = event.data;
for (const avatar of unresolvedAvatars) {
// Send urgent notification
await notifications.send({
userId: avatar.creatorId,
type: 'grace_period_final_warning',
urgency: 'critical',
title: 'URGENT: 2 Days Remaining',
message: `Your avatar "${avatar.name}" will be deactivated in 2 days unless you take action.`,
});
// Send email as backup
await email.send({
to: await getUserEmail(avatar.creatorId),
subject: '[URGENT] Your avatar will be deactivated in 2 days',
template: 'grace_period_final_warning',
data: { avatar, expiresAt },
});
// Also notify users
await notifyAvatarUsers(avatar.avatarId, {
type: 'avatar_expiring_soon',
message: 'This avatar will become unavailable in 2 days.',
suggestAlternatives: true,
});
}
}

Time to enforce:

async function handleGracePeriodExpired(event: GracePeriodExpiredEvent) {
const { gracePeriodId, avatarsToDeactivate } = event.data;
for (const avatar of avatarsToDeactivate) {
// Deactivate the avatar
await db.avatars.update(avatar.avatarId, {
status: 'deactivated',
deactivatedAt: new Date(),
deactivationReason: 'grace_period_expired',
});
// Notify creator
await notifications.send({
userId: avatar.creatorId,
type: 'avatar_deactivated',
title: 'Avatar Deactivated',
message: `Your avatar "${avatar.name}" has been deactivated due to identity protection.`,
});
// Migrate users to alternatives
await migrateUsersToAlternatives(avatar.avatarId);
}
}

When an avatar is deactivated, provide a smooth transition:

async function migrateUsersToAlternatives(avatarId: string) {
const users = await db.userAvatars.findByAvatar(avatarId);
const avatar = await db.avatars.get(avatarId);
// Find similar avatars
const alternatives = await findAlternativeAvatars(avatar);
for (const user of users) {
// Preserve conversation history
await archiveConversations(user.id, avatarId);
// Suggest alternatives
await notifications.send({
userId: user.id,
type: 'avatar_alternatives',
title: 'Your avatar is no longer available',
message: `${avatar.name} has been deactivated. We've saved your conversations and found similar avatars.`,
data: {
archivedConversations: `/conversations/archived/${avatarId}`,
alternatives: alternatives.map((a) => ({
id: a.id,
name: a.name,
similarity: a.similarityScore,
})),
},
});
}
}

Provide clear paths for avatar creators to resolve violations:

async function requestLicenseForAvatar(avatarId: string, tier: string) {
const avatar = await db.avatars.get(avatarId);
const request = await lmif.licenses.request({
boxId: avatar.gracePeriod.boxId,
tier,
useCase: `Avatar on ${PLATFORM_NAME}`,
estimatedUsers: avatar.userCount,
acceptGuidelines: true,
});
await db.avatars.update(avatarId, {
licenseRequest: {
id: request.id,
status: 'pending',
requestedAt: new Date(),
},
});
return request;
}
async function modifyAvatarToComply(avatarId: string, changes: AvatarChanges) {
// Update avatar to remove likeness
await db.avatars.update(avatarId, {
name: changes.newName,
imageUrl: changes.newImageUrl,
description: changes.newDescription,
});
// Re-check with LMIF
const check = await lmif.identity.check({
name: changes.newName,
imageUrl: changes.newImageUrl,
});
if (!check.isBoxed) {
// Resolve the violation
await lmif.gracePeriods.resolve(avatar.gracePeriod.id, {
resolution: 'modified',
avatarId,
});
await db.avatars.update(avatarId, {
status: 'active',
gracePeriod: null,
});
}
return check;
}
async function removeAvatarToComply(avatarId: string) {
const avatar = await db.avatars.get(avatarId);
// Soft delete
await db.avatars.update(avatarId, {
status: 'removed',
removedAt: new Date(),
removedReason: 'identity_protection',
});
// Notify LMIF
await lmif.gracePeriods.resolve(avatar.gracePeriod.id, {
resolution: 'removed',
avatarId,
});
// Handle users
await migrateUsersToAlternatives(avatarId);
}

Here’s a complete webhook handler implementation:

import express from 'express';
import { LMIFClient } from '@lookmaimfamous/lmif';
const app = express();
const lmif = new LMIFClient({ apiKey: process.env.LMIF_API_KEY });
app.post('/webhooks/lmif', express.raw({ type: 'application/json' }), async (req, res) => {
// Verify signature
const signature = req.headers['x-lmif-signature'] as string;
if (!lmif.webhooks.verify(req.body, signature, process.env.LMIF_WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
// Acknowledge immediately
res.status(200).send('OK');
// Process asynchronously
const event = JSON.parse(req.body.toString());
try {
switch (event.type) {
case 'box.created':
await handleBoxCreated(event);
break;
case 'box.updated':
await handleBoxUpdated(event);
break;
case 'violation.detected':
await handleViolationDetected(event);
break;
case 'grace_period.started':
await handleGracePeriodStarted(event);
break;
case 'grace_period.reminder':
await handleGracePeriodReminder(event);
break;
case 'grace_period.ending':
await handleGracePeriodEnding(event);
break;
case 'grace_period.expired':
await handleGracePeriodExpired(event);
break;
}
} catch (error) {
console.error(`Failed to handle ${event.type}:`, error);
// Consider alerting on failure
}
});