Appeals Workflow
Vettly provides built-in infrastructure for handling contested decisions. When users believe a decision was wrong, they deserve a fair process with complete transparency.
Why Appeals Matter
Appeals serve multiple purposes:
- Due process - Users deserve to contest decisions that affect them
- Policy tuning - Appeals reveal false positives and threshold issues
- Legal protection - Documented appeal processes protect against litigation
- Regulatory compliance - DSA Article 20 requires effective complaint handling
Appeal Lifecycle
Decision Made → User Appeals → Review Queue → Human Review → Resolution
↓
Re-evaluation
(optional)Initiating Appeals
User-Facing Appeal
typescript
// User submits an appeal
const appeal = await vettly.createAppeal({
decisionId: 'dec_abc123',
userId: 'user_123',
reason: 'This was a joke between friends, not harassment',
contactEmail: '[email protected]'
})
// appeal.id = "app_xyz789"
// appeal.status = "pending"Appeal with Evidence
typescript
const appeal = await vettly.createAppeal({
decisionId: 'dec_abc123',
userId: 'user_123',
reason: 'Context shows this was sarcasm',
evidence: [
{
type: 'url',
description: 'Previous message showing context',
url: 'https://platform.com/message/456'
}
]
})Review Queue
Listing Appeals
typescript
// Get pending appeals
const pending = await vettly.listAppeals({
status: 'pending',
sortBy: 'createdAt',
order: 'asc' // Oldest first
})
// Filter by category
const harassmentAppeals = await vettly.listAppeals({
status: 'pending',
category: 'harassment'
})
// Filter by priority (based on user history, content severity)
const highPriority = await vettly.listAppeals({
status: 'pending',
priority: 'high'
})Appeal Details
typescript
const appeal = await vettly.getAppeal('app_xyz789')
// Returns:
{
id: 'app_xyz789',
status: 'pending',
createdAt: '2024-01-15T10:00:00Z',
// Original decision
decision: {
id: 'dec_abc123',
action: 'block',
policy: { id: 'community-safe', version: '2.3.1' },
triggered: [{ category: 'harassment', score: 0.67, threshold: 0.4 }]
},
// User's appeal
user: {
id: 'user_123',
reason: 'This was a joke between friends',
evidence: []
},
// Content (if preserved)
content: {
type: 'text',
value: 'Original content here'
}
}Processing Appeals
Human Review
typescript
// Reviewer examines the appeal and makes a decision
const resolution = await vettly.resolveAppeal({
appealId: 'app_xyz789',
reviewerId: '[email protected]',
decision: 'upheld', // or 'overturned'
reason: 'Content violates community guidelines, decision stands',
notes: 'Reviewed context, but content is clearly targeted harassment'
})Overturning a Decision
typescript
const resolution = await vettly.resolveAppeal({
appealId: 'app_xyz789',
reviewerId: '[email protected]',
decision: 'overturned',
reason: 'Content was clearly satirical when viewed in context',
newAction: 'allow', // Change the action
restoreContent: true // Restore blocked content
})Re-evaluation
If policy has changed since the original decision:
typescript
// Check if policy has changed
const comparison = await vettly.comparePolicyVersions({
policy: 'community-safe',
from: appeal.decision.policy.version,
to: 'latest',
decisionId: appeal.decision.id
})
if (comparison.changed) {
// Re-evaluate under current policy
const reevaluated = await vettly.reevaluateForAppeal({
appealId: 'app_xyz789',
policyVersion: 'latest'
})
console.log({
originalAction: appeal.decision.action,
newAction: reevaluated.action,
reason: comparison.reason
})
}User Notification
Appeal Received
typescript
// Automatic notification when appeal is created
await vettly.setAppealNotifications({
onReceived: {
template: 'appeal-received',
channel: 'email'
}
})Appeal Resolved
typescript
await vettly.setAppealNotifications({
onResolved: {
template: 'appeal-resolved',
channel: 'email',
includeDecisionDetails: true
}
})Notification Templates
typescript
await vettly.setNotificationTemplate({
id: 'appeal-resolved',
subject: 'Your appeal has been reviewed',
body: `
Hello,
Your appeal regarding the {{decision.action}} of your content has been reviewed.
Decision: {{resolution.decision}}
Reason: {{resolution.reason}}
{{#if resolution.decision == 'overturned'}}
Your content has been restored.
{{else}}
If you believe this decision is still incorrect, you may contact us at [email protected]
{{/if}}
`
})Analytics
Appeal Metrics
typescript
const metrics = await vettly.getAppealMetrics({
from: '2024-01-01',
to: '2024-01-31'
})
// Returns:
{
total: 450,
pending: 45,
resolved: 405,
upheld: 360,
overturned: 45,
overturnRate: 0.111, // 11.1%
averageResolutionTime: '18h',
byCategory: {
harassment: { total: 125, overturned: 12 },
hate_speech: { total: 80, overturned: 4 },
// ...
}
}Policy Tuning Insights
typescript
const insights = await vettly.getAppealInsights({
from: '2024-01-01',
to: '2024-01-31'
})
// Returns suggestions for policy adjustments:
{
suggestions: [
{
category: 'harassment',
currentThreshold: 0.4,
suggestedThreshold: 0.45,
reason: 'High overturn rate (15%) suggests threshold too aggressive',
affectedDecisions: 250
}
]
}Compliance
DSA Requirements
The Digital Services Act requires:
- Effective complaint handling - Users must be able to contest decisions
- Free of charge - Appeals cannot require payment
- Timely resolution - Reasonable response times
- Human review - Automated decisions must have human review option
Vettly provides the infrastructure to meet all requirements.
Audit Trail
Every appeal is fully documented:
typescript
const audit = await vettly.getAppealAudit('app_xyz789')
// Returns complete history:
[
{ action: 'created', timestamp: '...', user: 'user_123' },
{ action: 'assigned', timestamp: '...', reviewer: 'admin@...' },
{ action: 'viewed', timestamp: '...', reviewer: 'admin@...' },
{ action: 'resolved', timestamp: '...', decision: 'upheld', reason: '...' },
{ action: 'notified', timestamp: '...', channel: 'email' }
]Next Steps
- Decision Records - Understand the original decision
- Policy Versioning - Re-evaluate under new policies
- DSA Compliance - Meet regulatory requirements
