Decision Records
Every Vettly judgment produces a decision record—a complete audit trail that can be retrieved by ID at any time.
Anatomy of a Decision
When you call vettly.check(), the response includes:
typescript
interface Decision {
// Unique identifier for this decision
id: string // "dec_abc123def456..."
// The action taken
action: 'allow' | 'warn' | 'flag' | 'block'
// Policy information
policy: {
id: string // "community-safe"
version: string // "v2.3.1"
}
// What triggered the action
triggered: {
category: string // "harassment"
threshold: number // 0.4
score: number // 0.67
}[]
// Raw provider response
provider: {
name: string // "openai"
model: string // "omni-moderation-latest"
scores: Record<string, number>
}
// Input verification
input: {
hash: string // SHA-256 of content
type: 'text' | 'image' | 'video'
}
// Context you provided
context: {
userId?: string
sessionId?: string
ipAddress?: string
custom?: Record<string, unknown>
}
// Timestamps
createdAt: string // ISO 8601
}Why Decision IDs Matter
For Appeals
When a user contests a decision:
typescript
// User clicks "Appeal this decision"
const decision = await vettly.getDecision(decisionId)
// Show them what happened
console.log(`
Your content was ${decision.action}ed because:
- Category: ${decision.triggered[0].category}
- Score: ${decision.triggered[0].score}
- Threshold: ${decision.triggered[0].threshold}
`)For Audits
When compliance needs a report:
typescript
// Export all decisions for a time period
const decisions = await vettly.listDecisions({
from: '2024-01-01',
to: '2024-01-31',
action: 'block'
})
// Generate compliance report
const report = decisions.map(d => ({
id: d.id,
action: d.action,
category: d.triggered[0]?.category,
timestamp: d.createdAt
}))For Legal Discovery
When lawyers need evidence:
typescript
// Retrieve specific decision for litigation
const decision = await vettly.getDecision(decisionId)
// Verify content hasn't changed
const currentHash = sha256(contentFromDb)
if (currentHash !== decision.input.hash) {
console.log('Content has been modified since decision')
}
// Show complete evidence chain
console.log({
contentHash: decision.input.hash,
policyVersion: decision.policy.version,
providerResponse: decision.provider,
actionTaken: decision.action,
timestamp: decision.createdAt
})Storing Decision IDs
Always store the decision ID alongside your content:
typescript
// When creating content
const decision = await vettly.check({ content: post.body })
await db.posts.create({
data: {
body: post.body,
authorId: user.id,
moderationDecisionId: decision.id, // Store this!
moderationAction: decision.action
}
})typescript
// When updating content
const decision = await vettly.check({ content: updatedBody })
await db.posts.update({
where: { id: post.id },
data: {
body: updatedBody,
moderationDecisionId: decision.id, // New decision for new content
moderationAction: decision.action
}
})Input Hashing
Every decision includes a SHA-256 hash of the input content:
typescript
const decision = await vettly.check({ content: 'Hello world' })
console.log(decision.input.hash)
// "a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e"This hash allows you to:
- Verify content integrity - Prove the decision was made on specific content
- Detect modifications - Know if content changed after the decision
- Support non-repudiation - Content author can't claim different content was evaluated
Retention and Deletion
Default Retention
Decisions are retained based on your plan:
| Plan | Retention |
|---|---|
| Developer | 24 hours |
| Starter | 7 days |
| Growth | 30 days |
| Pro | 90 days |
Legal Hold
For litigation, you can extend retention:
typescript
await vettly.setLegalHold({
decisionIds: ['dec_abc...', 'dec_def...'],
reason: 'Litigation hold - Case #12345',
expiry: '2025-12-31'
})GDPR Deletion
For right-to-erasure requests:
typescript
await vettly.deleteUserDecisions({
userId: 'user_123',
reason: 'GDPR erasure request'
})Next Steps
- Policy Versioning - Understand how policy versions work
- Audit Trails - Export and analyze decision history
- Appeals Workflow - Handle contested decisions
