@communitai/maasRepository SDK
TypeScript SDK
The official TypeScript SDK for CommunitAI's Moderation-as-a-Service API. Built with full type safety, automatic retries, and an intuitive API.
Installation
bash
# From the CommunitAI repository root
bun install
bun --filter @communitai/maas build
# From your application
bun add /path/to/communitai/packages/maasBuild the current repository SDK. Package publication is a separate release step.
Quick Start
typescript
import { CommunitAIModerator } from '@communitai/maas';
// Initialize the client
const client = new CommunitAIModerator({
apiKey: process.env.COMMUNITAI_API_KEY,
});
// Moderate content
const result = await client.moderate({
content: "Hello, world!",
contentType: "text",
});
console.log(result.decision); // "approve" | "reject" | "warn" | "review"
console.log(result.confidence);Configuration
The client accepts the following configuration options:
| Option | Type |
|---|---|
apiKeyrequired | string |
baseUrl | stringDefault: "https://www.communitai.org" |
timeout | numberDefault: 30000 |
maxRetries | numberDefault: 3 |
debug | booleanDefault: false |
typescript
const client = new CommunitAIModerator({
apiKey: process.env.COMMUNITAI_API_KEY,
timeout: 10000,
maxRetries: 5,
debug: false,
});Single Moderation
typescript
// Basic moderation
const result = await client.moderate({
content: "Check out my post!",
contentType: "text",
});
// With context and options
const contextualResult = await client.moderate({
content: "Buy now! Limited time offer!",
contentType: "text",
context: {
communityId: "marketplace",
authorReputation: 75,
metadata: {
postType: "listing",
category: "electronics",
},
},
rulesConfig: {
useGlobalSafety: true,
interpretation: "moderate",
},
options: {
explain: true,
suggestRewrite: true,
returnConfidence: true,
},
});
// Handle the result
if (contextualResult.decision === "approve") {
await publishPost(post);
} else if (contextualResult.decision === "reject") {
console.log("Rejected:", contextualResult.explanation);
if (contextualResult.suggestedRewrite) {
console.log("Suggested:", contextualResult.suggestedRewrite);
}
} else if (contextualResult.decision === "review") {
await queueForReview(post, contextualResult.requestId);
}Batch Moderation
typescript
// Submit a batch
const batch = await client.moderateBatch({
items: posts.map(post => ({
externalId: post.id,
content: post.body,
contentType: "text",
})),
priority: "normal",
});
console.log(`Submitted batch ${batch.jobId}`);
// Poll for status
let status = await client.getBatchStatus(batch.jobId);
while (status.status === "queued" || status.status === "processing") {
console.log(`Progress: ${status.items_processed}/${status.items_total}`);
await sleep(1000);
status = await client.getBatchStatus(batch.jobId);
}
// Get results
const completed = await client.getBatchStatus(batch.jobId, true);
for (const result of completed.results ?? []) {
console.log(`${result.externalId}: ${result.decision}`);
}
Custom Rules
typescript
// Create a rule
const rule = await client.createRule({
name: "No Spam Links",
description: "Block messages with suspicious links",
ruleType: "combined",
ruleDefinition: {
keywords: ["click here", "free money", "guaranteed"],
patterns: ["bit\\.ly\\/\\w+", "t\\.co\\/\\w+"],
semanticCategories: ["spam", "phishing"],
semanticThreshold: 0.7,
},
action: "reject",
severity: "moderate",
priority: 800,
});
// List rules
const rules = await client.listRules({ activeOnly: true });
// Update a rule
await client.updateRule(rule.id, {
isActive: false,
});
// Test a rule
const test = await client.testRule(rule.id, "Click here for FREE money! bit.ly/abc123");
console.log(`Triggered: ${test.triggered}`);Error Handling
The SDK provides typed error classes for common error scenarios:
typescript
import {
CommunitAIError,
AuthenticationError,
RateLimitError,
ValidationError,
QuotaExceededError,
ServerError,
} from '@communitai/maas';
try {
const result = await client.moderate({ content: "..." });
} catch (error) {
if (error instanceof AuthenticationError) {
// Invalid or missing API key
console.error("Auth failed:", error.message);
} else if (error instanceof RateLimitError) {
// Too many requests
console.error("Rate limit resets at:", error.resetAt);
} else if (error instanceof QuotaExceededError) {
// Monthly quota exceeded
console.error("Quota exceeded. Upgrade your plan.");
} else if (error instanceof ValidationError) {
// Invalid request parameters
console.error("Validation failed:", error.details);
} else if (error instanceof ServerError) {
// Server-side error
console.error("Server error:", error.message);
} else if (error instanceof CommunitAIError) {
// Other API error
console.error("API error:", error.code, error.message);
} else {
throw error;
}
}TypeScript Types
All types are exported for your convenience:
typescript
import type {
// Configuration
CommunitAIConfig,
// Moderation
ModerationRequest,
ModerationResult,
ModerationDecision,
ModerationContext,
ModerationOptions,
ContentType,
// Batch
BatchRequest,
BatchResponse,
BatchStatus,
BatchItem,
BatchItemResult,
// Rules
CustomRule,
CreateRuleRequest,
UpdateRuleRequest,
RuleDefinition,
RuleAction,
RuleType,
RuleInterpretation,
// Analytics
AnalyticsRequest,
AnalyticsResponse,
UsageAnalytics,
DecisionAnalytics,
LatencyAnalytics,
CustomRulesAnalytics,
// Webhooks
WebhookPayload,
WebhookEventType,
BatchCompletePayload,
} from '@communitai/maas';Best Practices
Reuse the client instance
Create one client instance and reuse it across your application.
// Good - create once
const client = new CommunitAIModerator({ apiKey });
export { client };
// Bad - creating new instances
async function moderate(content) {
const client = new CommunitAIModerator({ apiKey }); // Don't do this
return client.moderate({ content });
}Handle all decision types
Always handle all possible moderation decisions.
switch (result.decision) {
case "approve":
await publishContent();
break;
case "reject":
await notifyUser(result.explanation);
break;
case "warn":
await publishWithFlag();
break;
case "review":
await queueForHumanReview(result.requestId);
break;
default:
// TypeScript will catch if you miss a case
const _exhaustive: never = result.decision;
}Use webhooks for batches
Register an active customer webhook for moderation.batch.complete before using its URL. Polling needs no webhook.
// After registering this URL for the same customer
const notifiedBatch = await client.moderateBatch({
items: posts.map(post => ({ externalId: post.id, content: post.body })),
webhookUrl: "https://myapp.com/webhook",
});
// Polling also works without registering a webhook
const polledBatch = await client.moderateBatch({
items: posts.map(post => ({ externalId: post.id, content: post.body })),
});
const results = await pollUntilComplete(polledBatch.jobId);