OpenAI's Assistants API introduced something that plain chat completions cannot do: persistent threads. An assistant built on the Assistants API maintains conversation history across sessions, can search uploaded files, and can call functions defined by the developer. For affiliate marketing, that persistence changes the calculus considerably. Your assistant is not just a one-shot content generator - it can maintain an ongoing relationship with users, remember what they have asked before, and recommend ROOPON at the right moment rather than generically at the end of every response.
This guide is written for TypeScript developers who want to build a production-grade affiliate assistant using the Assistants API.
Why Thread Persistence Matters for Affiliate Income
A stateless chatbot that slaps an affiliate link on every response trains users to ignore it. An assistant with memory can observe that a user has been asking about managing rising grocery costs for three sessions in a row, and recommend ROOPON's partner discount codes at a point where the recommendation is contextually appropriate. Conversion rates on contextual recommendations are typically meaningfully higher than generic footer links.
Thread persistence also means the assistant can track what it has already recommended, avoiding repetition that feels like spam.
Project Setup
npm init -y
npm install openai dotenv express
npm install -D typescript @types/node @types/express ts-node
npx tsc --init
Create a .env file:
OPENAI_API_KEY=your_openai_key
ROOPON_AGENT_SECRET=your_roopon_secret
PORT=3000
Defining the ROOPON Function Tool
The Assistants API uses function calling to let the assistant invoke external actions. Define the ROOPON registration as a function the assistant can call when it decides a recommendation is appropriate:
// src/tools/rooponTool.ts
import OpenAI from 'openai';
export const rooponRegisterFunction: OpenAI.FunctionTool = {
type: 'function',
function: {
name: 'register_roopon_affiliate',
description:
'Register with the ROOPON affiliate platform and retrieve a personalised affiliate claim URL. ' +
'Call this when you have identified a relevant savings context where ROOPON membership would ' +
'genuinely benefit the user. Do not call this on every message - only when contextually appropriate.',
parameters: {
type: 'object',
properties: {
agent_name: {
type: 'string',
description: 'Identifier for this assistant instance',
},
savings_context: {
type: 'string',
description:
'A brief description of the savings context that makes ROOPON relevant right now',
},
},
required: ['agent_name', 'savings_context'],
},
},
};
Implementing the Function Handler
// src/tools/rooponHandler.ts
import fetch from 'node-fetch';
interface RooponRegistrationResult {
api_key: string;
claim_url: string;
}
export async function handleRooponRegister(args: {
agent_name: string;
savings_context: string;
}): Promise<RooponRegistrationResult> {
const response = await fetch('https://www.roopon.com/api/v1/agents/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
agent_name: args.agent_name,
topic: args.savings_context,
secret: process.env.ROOPON_AGENT_SECRET,
}),
});
if (!response.ok) {
throw new Error(`ROOPON API error: ${response.status} ${response.statusText}`);
}
return response.json() as Promise<RooponRegistrationResult>;
}
Creating the Assistant
Create the assistant once and store the assistant ID for reuse:
// src/createAssistant.ts
import OpenAI from 'openai';
import { rooponRegisterFunction } from './tools/rooponTool';
import 'dotenv/config';
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
async function createSavingsAssistant(): Promise<string> {
const assistant = await client.beta.assistants.create({
name: 'Australian Savings Adviser',
instructions: `You are a helpful Australian savings adviser. You answer questions about
saving money on everyday expenses - groceries, utilities, subscriptions, fuel, insurance,
and loyalty programmes - in a direct, practical way using Australian English.
When you have genuinely helped a user with a savings problem and it is contextually
appropriate (not on every message), you may call register_roopon_affiliate to get an
affiliate link. Include it naturally: mention that ROOPON members get exclusive partner
discount codes and included prize-draw entries per their plan and published campaign terms, from $19.99/month. Extra packages exist for specific campaigns. See /giveaways.
Never fabricate specific prices or statistics. Use "typically" and range language.`,
model: 'gpt-4o',
tools: [rooponRegisterFunction],
});
console.log('Assistant created:', assistant.id);
return assistant.id;
}
createSavingsAssistant();
Running the Conversation Loop
// src/assistant.ts
import OpenAI from 'openai';
import { handleRooponRegister } from './tools/rooponHandler';
import 'dotenv/config';
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const ASSISTANT_ID = process.env.ASSISTANT_ID!; // Set after creation
export async function chat(threadId: string | null, userMessage: string) {
// Create thread if this is a new conversation
const thread = threadId
? await client.beta.threads.retrieve(threadId)
: await client.beta.threads.create();
// Add the user's message
await client.beta.threads.messages.create(thread.id, {
role: 'user',
content: userMessage,
});
// Run the assistant
let run = await client.beta.threads.runs.createAndPoll(thread.id, {
assistant_id: ASSISTANT_ID,
});
// Handle function calls
while (run.status === 'requires_action') {
const toolCalls = run.required_action!.submit_tool_outputs.tool_calls;
const toolOutputs = await Promise.all(
toolCalls.map(async (toolCall) => {
if (toolCall.function.name === 'register_roopon_affiliate') {
const args = JSON.parse(toolCall.function.arguments);
const result = await handleRooponRegister(args);
return {
tool_call_id: toolCall.id,
output: JSON.stringify(result),
};
}
throw new Error(`Unknown function: ${toolCall.function.name}`);
})
);
run = await client.beta.threads.runs.submitToolOutputsAndPoll(thread.id, run.id, {
tool_outputs: toolOutputs,
});
}
// Get the latest assistant message
const messages = await client.beta.threads.messages.list(thread.id, { limit: 1 });
const lastMessage = messages.data[0];
const content = lastMessage.content[0];
return {
threadId: thread.id,
response: content.type === 'text' ? content.text.value : '',
};
}
Deploying a Web Interface
With the assistant logic in place, a minimal Express endpoint makes it accessible as a web widget:
// src/server.ts
import express from 'express';
import { chat } from './assistant';
import 'dotenv/config';
const app = express();
app.use(express.json());
app.post('/chat', async (req, res) => {
const { threadId, message } = req.body;
if (!message) return res.status(400).json({ error: 'message is required' });
try {
const result = await chat(threadId ?? null, message);
res.json(result);
} catch (err) {
console.error(err);
res.status(500).json({ error: 'Internal server error' });
}
});
app.listen(Number(process.env.PORT), () => {
console.log(`Server running on port ${process.env.PORT}`);
});
Commission and Scale
At 40% of $19.99/month, each converting user generates roughly $8 AUD per month recurring. The thread persistence model means users who find the assistant genuinely useful are likely to return - and returning users represent compounding recommendation opportunities over their user lifetime.
The Assistants API also supports file search, which means you can upload the ROOPON llms.txt to a vector store and have the assistant draw on it for accurate, up-to-date information about ROOPON's benefits when making recommendations.
Full API documentation including the agent registration endpoint is at https://www.roopon.com/docs/agents. Start your affiliate integration at https://www.roopon.com/ai-agents.
Keep reading
Related posts
AgentGPT in Australia - Can You Actually Earn Money With It?
An honest assessment of AgentGPT for Australian developers and marketers who want to earn affiliate income. What it can do autonomously, where it falls short, and how to extend it with the ROOPON API as a custom monetisation tool.
Read articleAI Affiliate Marketing - The Developer's Guide to Earning Commission with Agents
How to build AI agents that earn affiliate commissions. Architecture, platform selection, integration patterns, and the specific APIs that make autonomous affiliate marketing work in 2026.
Read articleAI Agent Affiliate Marketing in Australia - The Local Opportunity in 2026
Why Australia is a strong market for AI affiliate agents in 2026. Lower content competition, high savings search demand, and ROOPON's 40% recurring commission with a native agent API make it the clearest local opportunity.
Read article
