Skip to main content

AI / Jarvis Block

AI Block

Machine learning features for modern applications

The AI Block brings powerful machine learning capabilities to your application without the complexity of building ML infrastructure. Leverage pre-built AI features for text analysis, image recognition, recommendations, content moderation, and more—all through a simple REST API.

Features

  • Text Analysis - Sentiment, entities, key phrases, and language detection
  • Image Recognition - Classification, object detection, and OCR
  • Smart Recommendations - Personalized suggestions based on user behavior
  • Content Moderation - Detect inappropriate content and spam
  • Embeddings - Vector representations for semantic search and similarity
  • Language Translation - Translate text between 100+ languages
  • Summarization - Generate concise summaries of long text
  • Classification - Custom text and image classification

Quick Start

npm install @23blocks/sdk

API Endpoint

ServiceURL
AI (Jarvis)https://jarvis.api.us.23blocks.com

Environment Routing: Use the same URL for all environments. Your API key determines the environment:

  • pk_test_* / sk_test_* → Routes to Staging
  • pk_live_* / sk_live_* → Routes to Production
import { create23BlocksClient } from '@23blocks/sdk';

const client = create23BlocksClient({
urls: { ai: 'https://jarvis.api.us.23blocks.com' },
apiKey: 'your-api-key', // Use pk_test_* for staging, pk_live_* for production
});

// Analyze sentiment
const sentiment = await client.ai.analyzeSentiment({
text: 'This product is amazing!'
});
// { score: 0.92, label: 'positive' }

// Get recommendations
const recs = await client.ai.recommend({
userId: 'user_123',
context: 'product-page',
limit: 5
});

// Moderate content
const moderation = await client.ai.moderate({
text: 'Some user-generated content...'
});

Authentication

All API requests require authentication using your API credentials.

Required Headers

X-App-Id: your-app-id
X-Api-Key: your-api-key
Content-Type: application/json

User Identity Registration (Required)

Before You Start

Every user must be registered with the Jarvis Block before calling any endpoint. Without registration, all API calls return 404 with error code usr-not-registered. This is the most common integration issue.

Jarvis uses a register-once, execute-many pattern. Each user identity is registered once per tenant, then all subsequent API calls work using the user's unique_id from Auth Block.

Why Registration Is Required

Jarvis tracks per-user state: conversations, prompt executions, entity ownership, feedback, and usage limits. The registration step creates the internal identity record that links the Auth Block user to Jarvis resources.

Register a User

POST /identities/:user_unique_id/register

Self-registration — users can register themselves using their own JWT. No special scope required:

curl -X POST https://jarvis.api.us.23blocks.com/identities/usr_abc123/register \
-H "Authorization: Bearer user-jwt-token" \
-H "X-App-Id: your-app-id" \
-H "X-Api-Key: your-api-key" \
-H "Content-Type: application/json"

Admin registration — register a user on their behalf (requires identities:write scope):

curl -X POST https://jarvis.api.us.23blocks.com/identities/usr_abc123/register \
-H "Authorization: Bearer admin-jwt-token" \
-H "X-App-Id: your-app-id" \
-H "X-Api-Key: your-api-key" \
-H "Content-Type: application/json"

Response (201 Created):

{
"data": {
"id": "identity_abc123",
"type": "identity",
"attributes": {
"user_unique_id": "usr_abc123",
"status": "active",
"registered_at": "2026-05-20T10:00:00Z"
}
}
}

Calling register on an already-registered user returns the existing identity (idempotent).

The Error Without Registration

If you skip registration and call any Jarvis endpoint:

{
"error": {
"code": "usr-not-registered",
"message": "User not registered in this tenant",
"status": 404
}
}

Affected Endpoints

All Jarvis endpoints require a registered identity except the register endpoint itself:

Endpoint CategoryExamples
PromptsExecute, render, feedback
AgentsRuntime, threads, messages, runs
EntitiesCreate, update, query digital twins
Entity FilesUpload, list, query files on entities
ClustersCreate, manage entity groups
ConversationsCreate, list, message
IdentitiesAll endpoints except /register

Integration Pattern

Register users during your onboarding flow, right after authentication:

// After successful Auth Block login
const user = await authClient.signIn({ email, password });

// Register with Jarvis (idempotent — safe to call every login)
await fetch(`https://jarvis.api.us.23blocks.com/identities/${user.unique_id}/register`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${user.jwt}`,
'X-App-Id': APP_ID,
'X-Api-Key': API_KEY,
'Content-Type': 'application/json'
}
});

// Now all Jarvis endpoints work for this user
tip

Since registration is idempotent, you can safely call it on every login without checking if the user is already registered.


Text Analysis API

Analyze text for sentiment, entities, and more.

Analyze Sentiment

Determine the emotional tone of text.

POST /ai/v1/text/sentiment

Request Body:

{
"data": {
"type": "sentiment_requests",
"attributes": {
"text": "I absolutely love this product! It exceeded all my expectations.",
"language": "en"
}
}
}

Response:

{
"data": {
"type": "sentiment_results",
"id": "sent_abc123",
"attributes": {
"score": 0.92,
"label": "positive",
"confidence": 0.97,
"scores": {
"positive": 0.92,
"neutral": 0.06,
"negative": 0.02
}
}
}
}

Extract Entities

Identify named entities in text.

POST /ai/v1/text/entities

Request Body:

{
"data": {
"type": "entity_requests",
"attributes": {
"text": "Apple CEO Tim Cook announced new products at the event in Cupertino, California.",
"types": ["PERSON", "ORG", "LOCATION"]
}
}
}

Response:

{
"data": {
"type": "entity_results",
"id": "ent_xyz789",
"attributes": {
"entities": [
{
"text": "Apple",
"type": "ORG",
"start": 0,
"end": 5,
"confidence": 0.98
},
{
"text": "Tim Cook",
"type": "PERSON",
"start": 10,
"end": 18,
"confidence": 0.99
},
{
"text": "Cupertino",
"type": "LOCATION",
"start": 62,
"end": 71,
"confidence": 0.96
},
{
"text": "California",
"type": "LOCATION",
"start": 73,
"end": 83,
"confidence": 0.99
}
]
}
}
}

Entity Types:

TypeDescription
PERSONPeople, including fictional
ORGCompanies, agencies, institutions
LOCATIONCountries, cities, addresses
DATEDates and times
MONEYMonetary values
PRODUCTProducts and vehicles
EVENTNamed events
WORK_OF_ARTTitles of books, songs, etc.

Extract Key Phrases

Identify important phrases in text.

POST /ai/v1/text/key-phrases

Request Body:

{
"data": {
"type": "key_phrase_requests",
"attributes": {
"text": "The new machine learning algorithm significantly improves prediction accuracy while reducing computational costs.",
"max_phrases": 5
}
}
}

Response:

{
"data": {
"type": "key_phrase_results",
"attributes": {
"key_phrases": [
{ "phrase": "machine learning algorithm", "score": 0.95 },
{ "phrase": "prediction accuracy", "score": 0.89 },
{ "phrase": "computational costs", "score": 0.85 }
]
}
}
}

Detect Language

Identify the language of text.

POST /ai/v1/text/language

Request Body:

{
"data": {
"type": "language_requests",
"attributes": {
"text": "Bonjour, comment allez-vous?"
}
}
}

Response:

{
"data": {
"type": "language_results",
"attributes": {
"language": "fr",
"language_name": "French",
"confidence": 0.99,
"alternatives": [
{ "language": "it", "language_name": "Italian", "confidence": 0.01 }
]
}
}
}

Summarize Text

Generate a concise summary of long text.

POST /ai/v1/text/summarize

Request Body:

{
"data": {
"type": "summarize_requests",
"attributes": {
"text": "Your long article text here...",
"max_length": 100,
"style": "extractive"
}
}
}

Response:

{
"data": {
"type": "summarize_results",
"attributes": {
"summary": "A concise summary of the input text...",
"length": 85,
"compression_ratio": 0.15
}
}
}

Image Analysis API

Analyze images for classification, objects, and text.

Classify Image

Categorize an image.

POST /ai/v1/image/classify

Request Body:

{
"data": {
"type": "image_classify_requests",
"attributes": {
"image_url": "https://example.com/photo.jpg",
"max_labels": 5
}
}
}

Response:

{
"data": {
"type": "image_classify_results",
"attributes": {
"labels": [
{ "label": "dog", "confidence": 0.97 },
{ "label": "golden retriever", "confidence": 0.94 },
{ "label": "pet", "confidence": 0.92 },
{ "label": "animal", "confidence": 0.99 },
{ "label": "outdoor", "confidence": 0.78 }
]
}
}
}

Detect Objects

Identify and locate objects in an image.

POST /ai/v1/image/detect

Request Body:

{
"data": {
"type": "object_detect_requests",
"attributes": {
"image_url": "https://example.com/street.jpg",
"min_confidence": 0.7
}
}
}

Response:

{
"data": {
"type": "object_detect_results",
"attributes": {
"objects": [
{
"label": "car",
"confidence": 0.95,
"bounding_box": {
"x": 120,
"y": 200,
"width": 350,
"height": 180
}
},
{
"label": "person",
"confidence": 0.88,
"bounding_box": {
"x": 50,
"y": 100,
"width": 80,
"height": 250
}
}
]
}
}
}

Extract Text (OCR)

Extract text from images.

POST /ai/v1/image/ocr

Request Body:

{
"data": {
"type": "ocr_requests",
"attributes": {
"image_url": "https://example.com/document.jpg",
"detect_orientation": true
}
}
}

Response:

{
"data": {
"type": "ocr_results",
"attributes": {
"text": "Extracted text from the image...",
"blocks": [
{
"text": "INVOICE",
"confidence": 0.99,
"bounding_box": { "x": 50, "y": 20, "width": 150, "height": 30 }
},
{
"text": "Order #12345",
"confidence": 0.97,
"bounding_box": { "x": 50, "y": 60, "width": 200, "height": 25 }
}
],
"language": "en",
"orientation": 0
}
}
}

Recommendations API

Generate personalized recommendations.

Get Recommendations

POST /ai/v1/recommendations

Request Body:

{
"data": {
"type": "recommendation_requests",
"attributes": {
"user_id": "user_abc123",
"context": "product-page",
"item_id": "prod_xyz789",
"limit": 5,
"filters": {
"category": "electronics",
"price_max": 500
}
}
}
}

Response:

{
"data": {
"type": "recommendation_results",
"attributes": {
"recommendations": [
{
"item_id": "prod_001",
"score": 0.95,
"reason": "Users who viewed this also viewed"
},
{
"item_id": "prod_002",
"score": 0.87,
"reason": "Similar products"
},
{
"item_id": "prod_003",
"score": 0.82,
"reason": "Based on your history"
}
],
"model_version": "v2.3"
}
}
}

Track User Event

Record user interactions for better recommendations.

POST /ai/v1/recommendations/events

Request Body:

{
"data": {
"type": "recommendation_events",
"attributes": {
"user_id": "user_abc123",
"event_type": "purchase",
"item_id": "prod_xyz789",
"timestamp": "2025-01-15T10:30:00Z",
"metadata": {
"quantity": 1,
"price": 299.99
}
}
}
}

Event Types:

EventDescription
viewUser viewed an item
clickUser clicked on an item
add_to_cartItem added to cart
purchaseItem purchased
ratingUser rated an item
likeUser liked an item

Content Moderation API

Detect inappropriate content.

Moderate Text

POST /ai/v1/moderation/text

Request Body:

{
"data": {
"type": "moderation_requests",
"attributes": {
"text": "User-generated content to check...",
"categories": ["hate", "violence", "sexual", "spam"]
}
}
}

Response:

{
"data": {
"type": "moderation_results",
"attributes": {
"flagged": false,
"categories": {
"hate": { "flagged": false, "score": 0.02 },
"violence": { "flagged": false, "score": 0.01 },
"sexual": { "flagged": false, "score": 0.00 },
"spam": { "flagged": false, "score": 0.05 }
},
"action": "allow"
}
}
}

Moderate Image

POST /ai/v1/moderation/image

Request Body:

{
"data": {
"type": "image_moderation_requests",
"attributes": {
"image_url": "https://example.com/user-upload.jpg",
"categories": ["adult", "violence", "hate"]
}
}
}

Response:

{
"data": {
"type": "image_moderation_results",
"attributes": {
"flagged": false,
"categories": {
"adult": { "flagged": false, "score": 0.03 },
"violence": { "flagged": false, "score": 0.01 },
"hate": { "flagged": false, "score": 0.00 }
},
"action": "allow"
}
}
}

Embeddings API

Generate vector embeddings for semantic operations.

Generate Embeddings

POST /ai/v1/embeddings

Request Body:

{
"data": {
"type": "embedding_requests",
"attributes": {
"inputs": [
"Machine learning is transforming technology",
"AI is revolutionizing software development"
],
"model": "text-embedding-3"
}
}
}

Response:

{
"data": {
"type": "embedding_results",
"attributes": {
"embeddings": [
{
"index": 0,
"embedding": [0.023, -0.045, 0.089, ...],
"dimensions": 1536
},
{
"index": 1,
"embedding": [0.031, -0.052, 0.076, ...],
"dimensions": 1536
}
],
"model": "text-embedding-3",
"usage": {
"tokens": 18
}
}
}
}

Compute Similarity

Compare text similarity.

POST /ai/v1/embeddings/similarity

Request Body:

{
"data": {
"type": "similarity_requests",
"attributes": {
"source": "How do I reset my password?",
"targets": [
"I forgot my login credentials",
"Change my account password",
"What's the weather today?"
]
}
}
}

Response:

{
"data": {
"type": "similarity_results",
"attributes": {
"similarities": [
{ "index": 0, "score": 0.89 },
{ "index": 1, "score": 0.94 },
{ "index": 2, "score": 0.12 }
]
}
}
}

Translation API

Translate text between languages.

Translate Text

POST /ai/v1/translate

Request Body:

{
"data": {
"type": "translation_requests",
"attributes": {
"text": "Hello, how are you today?",
"source_language": "en",
"target_language": "es"
}
}
}

Response:

{
"data": {
"type": "translation_results",
"attributes": {
"translated_text": "Hola, como estas hoy?",
"source_language": "en",
"target_language": "es",
"confidence": 0.98
}
}
}

Supported Languages

Get list of supported languages.

GET /ai/v1/translate/languages

Classification API

Create custom classifiers.

Create Classifier

POST /ai/v1/classifiers

Request Body:

{
"data": {
"type": "classifiers",
"attributes": {
"name": "support-ticket-classifier",
"description": "Classify customer support tickets",
"labels": ["billing", "technical", "general", "urgent"]
}
}
}

Train Classifier

POST /ai/v1/classifiers/:classifier_id/train

Request Body:

{
"data": {
"type": "training_data",
"attributes": {
"examples": [
{ "text": "I was charged twice", "label": "billing" },
{ "text": "App keeps crashing", "label": "technical" },
{ "text": "How do I change settings?", "label": "general" },
{ "text": "URGENT: Service is down!", "label": "urgent" }
]
}
}
}

Classify Text

POST /ai/v1/classifiers/:classifier_id/classify

Request Body:

{
"data": {
"type": "classification_requests",
"attributes": {
"text": "My payment didn't go through"
}
}
}

Response:

{
"data": {
"type": "classification_results",
"attributes": {
"predictions": [
{ "label": "billing", "confidence": 0.92 },
{ "label": "technical", "confidence": 0.05 },
{ "label": "general", "confidence": 0.02 },
{ "label": "urgent", "confidence": 0.01 }
]
}
}
}

Prompt Rendering API

Render dynamic prompts with variable substitution and array transformations.

Render Prompt

Render a prompt template with placeholder values.

POST /ai/v1/prompts/:unique_id/render

Request Body:

{
"placeholders": {
"simple_string": "Hello",
"string_array": ["url1", "url2", "url3"],
"nested_object": {
"title": "My Title",
"metadata": { "author": "John" }
},
"object_array": [
{ "claim": "Fact 1", "priority": "high" },
{ "claim": "Fact 2", "priority": "low" }
]
}
}

Response:

{
"data": {
"type": "rendered_prompts",
"id": 12345,
"attributes": {
"prompt_unique_id": "prompt_abc123",
"rendered_content": "The rendered prompt text with substituted values...",
"placeholders_used": ["simple_string", "string_array", "nested_object"]
}
}
}
Breaking Change

The response id field is now an integer (previously UUID). Use prompt_unique_id for the UUID identifier.

Placeholder Syntax

PatternExampleDescription
Simple{{name}}Basic variable substitution
Nested object{{user.email}}Access nested object fields
Array index{{items[0]}}Access array element by index
Combined{{steps[0].title}}Index access + field access

Pipe Transforms for Arrays

Transform array values using pipe syntax: {{placeholder\|transform}}

TransformSyntaxInputOutput
Default{{urls}}["a", "b"]a\nb (newline-joined)
Bullets{{urls|bullets}}["a", "b"]- a\n- b
Numbered{{urls|numbered}}["a", "b"]1. a\n2. b
Join{{urls|join:, }}["a", "b"]a, b
Length{{urls|length}}["a", "b"]2
First{{urls|first}}["a", "b"]a
Last{{urls|last}}["a", "b"]b
Field{{users|field:name}}[{name: "John"}, {name: "Jane"}]John\nJane

Examples

Simple variable substitution:

Prompt template: "Hello {{name}}, welcome to {{company}}!"
Placeholders: { "name": "Alice", "company": "Acme Corp" }
Result: "Hello Alice, welcome to Acme Corp!"

Array with bullets:

Prompt template: "Sources:\n{{urls|bullets}}"
Placeholders: { "urls": ["https://example.com", "https://test.com"] }
Result: "Sources:\n- https://example.com\n- https://test.com"

Nested object access:

Prompt template: "Author: {{metadata.author}}, Published: {{metadata.date}}"
Placeholders: { "metadata": { "author": "John Doe", "date": "2026-01-15" } }
Result: "Author: John Doe, Published: 2026-01-15"

Object array with field extraction:

Prompt template: "Claims to verify:\n{{claims|field:text|numbered}}"
Placeholders: { "claims": [{ "text": "Fact 1" }, { "text": "Fact 2" }] }
Result: "Claims to verify:\n1. Fact 1\n2. Fact 2"

Prompt Templates (PromptHub)

The AI Block includes PromptHub — a library of 10 research-backed prompt engineering templates. Each template provides structured sections, help text, and {{placeholder}} substitution for dynamic values.

Template Library

Templates are organized into three tiers:

Universal Templates

TemplateDescriptionKey Sections
FreeformOpen-ended prompt for any taskSystem instructions, user input
Persona AgentRole-based prompts with expertise contextRole definition, expertise areas, behavioral guidelines, constraints
Structured OutputEnforce JSON, tables, or specific formatsOutput schema, format rules, validation criteria

Reasoning & Quality Templates

TemplateDescriptionKey Sections
Chain of ThoughtStep-by-step reasoning for complex problemsProblem statement, reasoning steps, conclusion format
Few-Shot LearningTeach by example with input/output pairsExamples (input/output), task instruction, output format
Self-CritiqueLLM reviews and improves its own outputInitial task, critique criteria, revision instructions

Specialized Templates

TemplateDescriptionKey Sections
RAG ContextGrounded generation with retrieved documentsContext documents, query, citation format, source attribution
Research QueryMulti-source research with structured findingsResearch topic, source types, findings format, evidence requirements
Fact CheckVerify claims against evidenceClaims list, evidence sources, confidence scoring, verdict format
Knowledge GenerationGenerate structured knowledge from unstructured dataInput data, knowledge schema, extraction rules, output structure

List Available Templates

GET /prompts

Returns all prompt templates available in your tenant, including PromptHub defaults and any custom prompts.

curl -X GET https://jarvis.api.us.23blocks.com/prompts \
-H "Authorization: Bearer your-jwt-token" \
-H "X-App-Id: your-app-id" \
-H "X-Api-Key: your-api-key"

Create from Template

POST /prompts

Create a new prompt based on a PromptHub template or from scratch:

curl -X POST https://jarvis.api.us.23blocks.com/prompts \
-H "Authorization: Bearer your-jwt-token" \
-H "X-App-Id: your-app-id" \
-H "X-Api-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"name": "Customer Support Classifier",
"content": "You are a support ticket classifier.\n\nClassify the following ticket into one of these categories: {{categories|join:, }}\n\nTicket: {{ticket_text}}\n\nRespond with JSON: {\"category\": \"...\", \"confidence\": 0.0-1.0}",
"description": "Classifies support tickets using structured output"
}'

Execute a Prompt

POST /prompts/:unique_id/execute

Render and execute a prompt against a configured LLM in one call:

curl -X POST https://jarvis.api.us.23blocks.com/prompts/prompt_abc123/execute \
-H "Authorization: Bearer your-jwt-token" \
-H "X-App-Id: your-app-id" \
-H "X-Api-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"placeholders": {
"categories": ["billing", "technical", "general", "urgent"],
"ticket_text": "I was charged twice for my subscription"
}
}'

Response:

{
"data": {
"type": "prompt_executions",
"id": "exec_xyz789",
"attributes": {
"prompt_unique_id": "prompt_abc123",
"rendered_content": "You are a support ticket classifier...",
"response": "{\"category\": \"billing\", \"confidence\": 0.95}",
"model": "gpt-4o",
"tokens_used": 142,
"execution_time_ms": 820
}
}
}

Getting Started with PromptHub

  1. Register your user with Jarvis (see User Identity Registration)
  2. Browse templatesGET /prompts returns all available templates
  3. Render a promptPOST /prompts/:uid/render with your placeholder values
  4. Execute against LLMPOST /prompts/:uid/execute to render + run in one call
  5. Iterate — Create versions, test with different inputs, compare results
tip

All 10 PromptHub templates are pre-loaded in every tenant. You can use them directly or clone them as starting points for custom prompts.


Webhooks

Configure webhooks for AI events.

Register Webhook

POST /ai/v1/webhooks

Request Body:

{
"data": {
"type": "webhooks",
"attributes": {
"url": "https://your-app.com/webhooks/ai",
"events": [
"moderation.flagged",
"classifier.trained",
"batch.completed"
],
"secret": "your-webhook-secret"
}
}
}

Webhook Events

EventDescription
moderation.flaggedContent was flagged by moderation
classifier.trainedCustom classifier finished training
batch.completedBatch processing job completed

Error Handling

Error Response Format

{
"errors": [
{
"status": "400",
"code": "invalid_image_format",
"title": "Invalid Image Format",
"detail": "The image format 'bmp' is not supported. Supported formats: jpg, png, gif, webp.",
"source": {
"parameter": "image_url"
}
}
]
}

Common Error Codes

CodeStatusDescription
text_too_long400Input text exceeds maximum length
invalid_image_format400Unsupported image format
invalid_language400Language code not supported
classifier_not_found404Custom classifier does not exist
model_unavailable503AI model temporarily unavailable
rate_limit_exceeded429Too many requests

SDK Examples

React Sentiment Component

import { useState } from 'react';
import { create23BlocksClient } from '@23blocks/sdk';

const client = create23BlocksClient({
urls: { ai: 'https://jarvis.api.us.23blocks.com' },
appId: 'your-app-id',
});

function SentimentAnalyzer() {
const [text, setText] = useState('');
const [sentiment, setSentiment] = useState(null);
const [loading, setLoading] = useState(false);

const analyzeSentiment = async () => {
if (!text.trim()) return;

setLoading(true);
try {
const result = await client.ai.analyzeSentiment({ text });
setSentiment(result);
} finally {
setLoading(false);
}
};

const getSentimentColor = (label) => {
switch (label) {
case 'positive': return 'text-green-500';
case 'negative': return 'text-red-500';
default: return 'text-gray-500';
}
};

return (
<div className="sentiment-analyzer">
<textarea
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="Enter text to analyze..."
rows={4}
/>
<button onClick={analyzeSentiment} disabled={loading}>
{loading ? 'Analyzing...' : 'Analyze Sentiment'}
</button>

{sentiment && (
<div className="result">
<p className={getSentimentColor(sentiment.label)}>
{sentiment.label.toUpperCase()} ({(sentiment.score * 100).toFixed(1)}%)
</p>
<div className="confidence-bar">
<div
style={{ width: `${sentiment.confidence * 100}%` }}
className="confidence-fill"
/>
</div>
</div>
)}
</div>
);
}

Content Moderation Hook

import { useState, useCallback } from 'react';
import { create23BlocksClient } from '@23blocks/sdk';

const client = create23BlocksClient({
urls: { ai: 'https://jarvis.api.us.23blocks.com' },
appId: 'your-app-id',
});

function useContentModeration() {
const [isChecking, setIsChecking] = useState(false);

const moderateContent = useCallback(async (content: string) => {
setIsChecking(true);
try {
const result = await client.ai.moderate({
text: content,
categories: ['hate', 'violence', 'sexual', 'spam']
});

return {
isApproved: !result.flagged,
flags: Object.entries(result.categories)
.filter(([_, data]) => data.flagged)
.map(([category]) => category),
action: result.action
};
} finally {
setIsChecking(false);
}
}, []);

return { moderateContent, isChecking };
}

// Usage in a form
function CommentForm() {
const { moderateContent, isChecking } = useContentModeration();
const [comment, setComment] = useState('');
const [error, setError] = useState('');

const handleSubmit = async (e) => {
e.preventDefault();
setError('');

const moderation = await moderateContent(comment);
if (!moderation.isApproved) {
setError(`Comment flagged for: ${moderation.flags.join(', ')}`);
return;
}

// Submit the comment...
};

return (
<form onSubmit={handleSubmit}>
<textarea
value={comment}
onChange={(e) => setComment(e.target.value)}
placeholder="Write a comment..."
/>
{error && <p className="error">{error}</p>}
<button type="submit" disabled={isChecking}>
{isChecking ? 'Checking...' : 'Post Comment'}
</button>
</form>
);
}

Rate Limits

PlanRequests/minuteText chars/requestImages/minute
Free605,00010
Starter30025,00060
Growth1,000100,000200
EnterpriseCustomCustomCustom

Best Practices

Text Analysis

  1. Batch when possible - Send multiple texts in a single request
  2. Specify language - Improves accuracy when you know the language
  3. Clean input - Remove irrelevant formatting or markup

Image Analysis

  1. Optimize images - Resize large images before sending
  2. Use URLs - Prefer image URLs over base64 for large images
  3. Set confidence thresholds - Filter low-confidence results

Recommendations

  1. Track events - The more user events, the better recommendations
  2. Use context - Provide page/section context for relevance
  3. Apply filters - Filter by availability, category, etc.

Content Moderation

  1. Moderate early - Check content before displaying to others
  2. Use appropriate categories - Only check relevant categories
  3. Handle flags gracefully - Provide helpful feedback to users