Overview
This guide walks through the complete process of generating commentary from your licensed personas.
Human-in-the-Loop Quality : Unleeshed uses a human-in-the-loop workflow where creators inject their authentic perspective before AI generates commentary. This ensures high-fidelity responses but means generation is measured in hours, not seconds.
The Workflow
What Happens Behind the Scenes
Topic Created → Creators receive email notification
Opinion Injected → Creator adds their unique perspective (human step)
AI Generates → Commentary generated with creator’s voice
Creator Approves → Quality check before delivery (human step)
Ready to Fetch → Available via API
Step 1: Get Your Licensed Personas
First, retrieve the personas you have access to:
async function getPersonas () {
const response = await fetch ( 'https://prod.api.unleeshed.ai/partner/v1/personas' , {
headers: { 'X-Api-Key' : process . env . UNLEESHED_API_KEY }
});
const { data } = await response . json ();
return data ;
}
const personas = await getPersonas ();
// Store persona IDs for later use
const personaIds = personas . map ( p => p . id );
Cache persona data for 1 hour to reduce API calls.
Step 2: Create a Topic
Submit your topic with selected personas:
async function createTopic ( content , personaIds , options = {}) {
const response = await fetch ( 'https://prod.api.unleeshed.ai/partner/v1/topics' , {
method: 'POST' ,
headers: {
'X-Api-Key' : process . env . UNLEESHED_API_KEY ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
content ,
persona_ids: personaIds ,
output_types: options . outputTypes || [ 'text' ],
metadata: options . metadata || {}
})
});
if ( ! response . ok ) {
const error = await response . json ();
throw new Error ( error . error . message );
}
const { data } = await response . json ();
return data ;
}
const topic = await createTopic (
"Should the Lakers trade Anthony Davis before the deadline?" ,
personaIds ,
{
outputTypes: [ 'text' ],
metadata: { source: 'daily_poll' }
}
);
console . log ( `Topic created: ${ topic . topic_id } ` );
console . log ( `Personas: ${ topic . personas_sent } will generate commentary` );
Handling Per-Persona Results
The response includes status for each persona:
{
"topic_id" : "abc123..." ,
"personas_sent" : 2 ,
"results" : [
{ "persona_id" : "..." , "status" : "sent" },
{ "persona_id" : "..." , "status" : "sent" }
]
}
Status Meaning Action sentSuccessfully queued Wait for generation not_licensedNo active license Contact support duplicateSame topic sent within 24h Use existing topic
Step 3: Poll for Status
Since commentary generation involves human creators, it takes longer than instant AI. Use the lightweight status endpoint for efficient polling:
async function pollForCommentaries ( topicId , options = {}) {
const {
pollIntervalMs = 60000 , // Poll every 60 seconds (human timescale)
allowPartial = false
} = options ;
while ( true ) {
// Use the lightweight status endpoint for polling
const response = await fetch (
`https://prod.api.unleeshed.ai/partner/v1/topics/ ${ topicId } /status` ,
{ headers: { 'X-Api-Key' : process . env . UNLEESHED_API_KEY } }
);
const { data } = await response . json ();
// Log per-persona progress
console . log ( `Overall: ${ data . overall_status } ` );
data . personas . forEach ( p => {
console . log ( ` ${ p . persona_name } : ${ p . status } ` );
});
// All done
if ( data . overall_status === 'completed' ) {
return data ;
}
// Some ready - can return partial results
if ( data . overall_status === 'partial' && allowPartial ) {
return data ;
}
// Wait before next poll
await new Promise ( r => setTimeout ( r , pollIntervalMs ));
}
}
const status = await pollForCommentaries ( topic . topic_id , { allowPartial: true });
Realistic Expectations : Human-in-the-loop generation typically takes 30 minutes to several hours , not seconds. Set appropriate expectations in your UI and consider webhook notifications for production use.
Per-Persona Status Values
Status Description Has Commentary topic_sentWaiting for creator No opinion_injectedCreator responded, AI generating No commentary_generatedAwaiting creator approval No creator_approvedReady to fetch Yes declinedCreator passed on this topic No expiredCreator didn’t respond in time No
Overall Status Values
Status Description pendingAll personas still waiting in_progressAt least one creator is working partialSome ready, others in progress completedAll personas finished
Once ready, get the commentary content:
async function getCommentaries ( topicId ) {
const response = await fetch (
`https://prod.api.unleeshed.ai/partner/v1/topics/ ${ topicId } /commentaries` ,
{ headers: { 'X-Api-Key' : process . env . UNLEESHED_API_KEY } }
);
const { data } = await response . json ();
return data . commentaries ;
}
const commentaries = await getCommentaries ( topic . topic_id );
commentaries . forEach ( c => {
console . log ( ` ${ c . persona . name } : ${ c . content . substring ( 0 , 100 ) } ...` );
});
Complete Example
Here’s a complete integration:
class UnleeshedClient {
constructor ( apiKey ) {
this . apiKey = apiKey ;
this . baseUrl = 'https://prod.api.unleeshed.ai/partner/v1' ;
}
async request ( path , options = {}) {
const response = await fetch ( ` ${ this . baseUrl }${ path } ` , {
... options ,
headers: {
'X-Api-Key' : this . apiKey ,
'Content-Type' : 'application/json' ,
... options . headers
}
});
const data = await response . json ();
if ( ! response . ok ) {
throw new Error ( data . error ?. message || 'Request failed' );
}
return data ;
}
async getPersonas () {
const { data } = await this . request ( '/personas' );
return data ;
}
async createTopic ( content , personaIds , outputTypes = [ 'text' ]) {
const { data } = await this . request ( '/topics' , {
method: 'POST' ,
body: JSON . stringify ({ content , persona_ids: personaIds , output_types: outputTypes })
});
return data ;
}
async getTopicWithCommentaries ( topicId ) {
const { data } = await this . request ( `/topics/ ${ topicId } ` );
return data ;
}
async generateCommentary ( content , personaIds , options = {}) {
// Create topic
const topic = await this . createTopic ( content , personaIds , options . outputTypes );
// Wait for completion
const maxWait = options . maxWaitMs || 180000 ;
const pollInterval = options . pollIntervalMs || 10000 ;
const start = Date . now ();
while ( Date . now () - start < maxWait ) {
const result = await this . getTopicWithCommentaries ( topic . topic_id );
if ( result . status === 'completed' ) {
return result ;
}
await new Promise ( r => setTimeout ( r , pollInterval ));
}
throw new Error ( 'Timeout' );
}
}
// Usage
const client = new UnleeshedClient ( process . env . UNLEESHED_API_KEY );
const personas = await client . getPersonas ();
const result = await client . generateCommentary (
"Should the Lakers trade AD?" ,
personas . map ( p => p . id )
);
console . log ( result . commentaries );
Next Steps
Display Commentaries Best practices for showing commentaries on your platform.
Error Handling Handle edge cases gracefully.