Overview
The app.bsky.labeler namespace provides lexicons for declaring and querying labeling services. Labelers apply content moderation labels to posts, profiles, and other content.
Key Concepts
- Labeler: A service that applies content labels
- Labels: Metadata tags indicating content properties (NSFW, spam, etc.)
- Labeling Policies: Which labels a service can apply
- Custom Labels: Service-specific label definitions
- Subscriptions: Users subscribing to labeler services
Record Types
service
Labeler service declaration.
Record Structure:
Labeling policies including supported label values
Service creation timestamp
Example:
await agent.com.atproto.repo.putRecord({
repo: agent.session.did,
collection: 'app.bsky.labeler.service',
rkey: 'self',
record: {
$type: 'app.bsky.labeler.service',
policies: {
labelValues: ['porn', 'sexual', 'graphic-media', 'custom-label'],
labelValueDefinitions: [
{
identifier: 'custom-label',
severity: 'alert',
blurs: 'content',
defaultSetting: 'warn',
adultOnly: false,
locales: [
{
lang: 'en',
name: 'Custom Label',
description: 'Content flagged by our custom filter'
}
]
}
]
},
createdAt: new Date().toISOString()
}
})
Queries
getServices
Get information about labeler services.
Endpoint: app.bsky.labeler.getServices
Array of labeler service DIDs (max 25)
Return detailed view with policies
Response:
Array of labeler service views
Example:
const response = await agent.app.bsky.labeler.getServices({
dids: ['did:plc:labeler123'],
detailed: true
})
for (const labeler of response.data.views) {
console.log('Labeler:', labeler.creator.handle)
console.log('Like count:', labeler.likeCount)
if (labeler.policies) {
console.log('Supported labels:', labeler.policies.labelValues)
}
}
curl "https://bsky.social/xrpc/app.bsky.labeler.getServices?dids=did:plc:labeler123&detailed=true"
Type Definitions
labelerView
Basic labeler service view.
AT-URI of the labeler service record
CID of the service record
Profile of the labeler service operator
Number of users subscribed to this labeler
Viewer’s relationship to the labeler
When the service was indexed
Labels applied to the labeler service itself
labelerViewDetailed
Detailed labeler service view with policies.
Includes all fields from labelerView plus:
Report reason types this service accepts
Subject types this service accepts reports for (account, record, chat)
Record collection NSIDs this service labels
labelerPolicies
Labeling policies for a service.
Array of label value strings this service can apply
Definitions for custom labels created by this service
Example:
{
labelValues: [
'porn',
'sexual',
'graphic-media',
'spam',
'custom-spoilers'
],
labelValueDefinitions: [
{
identifier: 'custom-spoilers',
severity: 'inform',
blurs: 'content',
defaultSetting: 'warn',
adultOnly: false,
locales: [
{
lang: 'en',
name: 'Spoilers',
description: 'Content contains spoilers for recent media'
}
]
}
]
}
labelerViewerState
Viewer’s relationship to a labeler.
AT-URI of like record if viewer has subscribed to this labeler
Using Labelers
Labelers are managed through user preferences in app.bsky.actor.defs#labelersPref.
Subscribe to a Labeler
// Get current preferences
const { data } = await agent.app.bsky.actor.getPreferences()
// Find or create labelers preference
let labelersPref = data.preferences.find(
p => p.$type === 'app.bsky.actor.defs#labelersPref'
)
if (!labelersPref) {
labelersPref = {
$type: 'app.bsky.actor.defs#labelersPref',
labelers: []
}
data.preferences.push(labelersPref)
}
// Add labeler
labelersPref.labelers.push({
did: 'did:plc:labeler123'
})
// Save preferences
await agent.app.bsky.actor.putPreferences({
preferences: data.preferences
})
Unsubscribe from a Labeler
// Get current preferences
const { data } = await agent.app.bsky.actor.getPreferences()
// Find labelers preference
const labelersPref = data.preferences.find(
p => p.$type === 'app.bsky.actor.defs#labelersPref'
)
if (labelersPref) {
// Remove labeler
labelersPref.labelers = labelersPref.labelers.filter(
l => l.did !== 'did:plc:labeler123'
)
// Save preferences
await agent.app.bsky.actor.putPreferences({
preferences: data.preferences
})
}
Check Subscribed Labelers
const { data } = await agent.app.bsky.actor.getPreferences()
const labelersPref = data.preferences.find(
p => p.$type === 'app.bsky.actor.defs#labelersPref'
)
if (labelersPref) {
const labelerDids = labelersPref.labelers.map(l => l.did)
// Get detailed info about subscribed labelers
const labelers = await agent.app.bsky.labeler.getServices({
dids: labelerDids,
detailed: true
})
for (const labeler of labelers.data.views) {
console.log('Subscribed to:', labeler.creator.handle)
console.log('Labels:', labeler.policies?.labelValues)
}
}
Creating a Labeler Service
To create a labeler service:
- Declare the Service: Create a
app.bsky.labeler.service record
- Implement Label Emission: Emit labels via
com.atproto.label.subscribeLabels
- Configure DID Document: Add labeler service endpoint to DID document
- Accept Reports (optional): Implement
com.atproto.moderation.createReport handling
Example Service Declaration
const service = await agent.com.atproto.repo.putRecord({
repo: agent.session.did,
collection: 'app.bsky.labeler.service',
rkey: 'self',
record: {
$type: 'app.bsky.labeler.service',
policies: {
labelValues: [
// Global labels
'porn',
'sexual',
'nudity',
'graphic-media',
// Custom labels
'spoilers',
'politics',
'sports-scores'
],
labelValueDefinitions: [
{
identifier: 'spoilers',
severity: 'inform',
blurs: 'content',
defaultSetting: 'warn',
adultOnly: false,
locales: [
{
lang: 'en',
name: 'Spoilers',
description: 'Content may contain spoilers'
}
]
},
{
identifier: 'politics',
severity: 'inform',
blurs: 'none',
defaultSetting: 'show',
adultOnly: false,
locales: [
{
lang: 'en',
name: 'Political Content',
description: 'Content related to politics'
}
]
}
]
},
createdAt: new Date().toISOString()
}
})
console.log('Labeler service created:', service.uri)
Label Severity Levels
When defining custom labels, you can set severity:
inform: Informational only, content not hidden by default
alert: Warning shown, content blurred by default
none: No special handling
Label Blur Settings
Controls what gets blurred when label is applied:
content: Blur the content itself
media: Blur embedded media only
none: Don’t blur anything
Default Settings
How labels are treated by default:
hide: Content hidden by default
warn: Content shown with warning
show: Content shown normally
ignore: Label ignored
Common Use Cases
Find Labelers to Subscribe
// Search for labelers
const results = await agent.app.bsky.actor.searchActors({
q: 'labeler'
})
// Check if they're running a labeler service
for (const actor of results.data.actors) {
const labelers = await agent.app.bsky.labeler.getServices({
dids: [actor.did],
detailed: true
})
if (labelers.data.views.length > 0) {
const labeler = labelers.data.views[0]
console.log('Found labeler:', actor.handle)
console.log('Provides labels:', labeler.policies?.labelValues)
}
}
Combine with content label preferences:
const { data } = await agent.app.bsky.actor.getPreferences()
// Subscribe to labeler
let labelersPref = data.preferences.find(
p => p.$type === 'app.bsky.actor.defs#labelersPref'
)
if (!labelersPref) {
labelersPref = { $type: 'app.bsky.actor.defs#labelersPref', labelers: [] }
data.preferences.push(labelersPref)
}
labelersPref.labelers.push({ did: 'did:plc:labeler123' })
// Configure how to handle their custom labels
data.preferences.push({
$type: 'app.bsky.actor.defs#contentLabelPref',
labelerDid: 'did:plc:labeler123',
label: 'spoilers',
visibility: 'warn'
})
data.preferences.push({
$type: 'app.bsky.actor.defs#contentLabelPref',
labelerDid: 'did:plc:labeler123',
label: 'politics',
visibility: 'hide'
})
await agent.app.bsky.actor.putPreferences({
preferences: data.preferences
})
Resources