Most of us have experienced how AI agents can automate our own work. But packaging that automation as a product you can sell to clients is a different challenge entirely.
Automating your own tasks is one thing. Building a reliable, billable automation service that generates the same value for dozens of clients month after month is engineering at a different scale. This article walks you through how to architect and implement agent-powered services using Antigravity's AgentKit 2.0, with working code examples for three production-ready business models.
From Personal Automation to Sellable Products
The gap between personal automation and commercialized agent services hinges on three structural requirements.
Personal automation: If your workflow breaks, you fix it yourself. An 80% accuracy rate is fine if you're the only user.
Commercialized product: A client is paying monthly for reliability. A 95% success rate might trigger refund requests. Accuracy becomes non-negotiable.
The three pillars of agent commercialization are:
- Reliability — Graceful error handling, retry logic, and fallback responses ensure the service continues functioning even when individual agent calls fail
- Measurability — Every execution is logged with token counts, execution time, and outcome, enabling usage-based billing and accurate cost attribution
- Transparency — Clients see exactly what they're paying for via a dashboard showing execution history, token consumption, and monthly charges
Without these three pillars, agents remain internal tools. With them, they become SaaS products.
AgentKit 2.0 Multi-Agent Orchestration Architecture
Antigravity's AgentKit 2.0 introduces the Manager Surface pattern, which lets you coordinate multiple specialist agents. This is the architectural foundation for scaling agent services.
How Manager Surface Works
The Manager Agent acts as a coordinator, deciding which specialist agents to invoke, in what order, and how to aggregate results. For client-facing services, this separation of concerns is crucial—clients never call agents directly; they call your API, which routes to the appropriate agent through the manager.
// manager-orchestrator.js
// Multi-agent orchestration using Manager Surface
const Anthropic = require("@anthropic-ai/sdk");
const client = new Anthropic();
const managerPrompt = `You are a Manager Agent coordinating multiple specialist agents for client automation.
Available Specialists:
- ReportGenerator: Creates weekly/monthly business reports from data sources
- DataAnalyzer: Extracts insights and segments from datasets
- ContentProducer: Generates marketing content at scale
For each client request:
1. Determine required specialists based on request type
2. Validate execution capacity (max 10 concurrent operations)
3. Delegate to appropriate specialists
4. Aggregate results and validate quality
5. Return structured response with audit trail`;
async function orchestrateRequest(clientId, request, executionLog) {
const response = await client.messages.create({
model: "claude-3-5-sonnet-20241022",
max_tokens: 2048,
system: managerPrompt,
messages: [
{
role: "user",
content: `Client: ${clientId}\nRequest: ${request}\nExecution Context: ${JSON.stringify(executionLog)}`,
},
],
});
const usage = response.usage;
// Critical: Log token consumption for billing
executionLog.push({
timestamp: new Date().toISOString(),
agentType: "Manager",
inputTokens: usage.input_tokens,
outputTokens: usage.output_tokens,
totalTokens: usage.input_tokens + usage.output_tokens,
clientId: clientId,
operation: "orchestration",
});
return {
decision: response.content[0].text,
executionLog: executionLog,
usage: usage,
};
}
module.exports = { orchestrateRequest };Resilience: Retry Logic and Fallbacks
In production, agent calls fail. Network timeouts, API limits, momentary service disruptions—these happen. A commercialized service doesn't crash when one execution fails; it recovers gracefully.
// agent-resilience.js
// Production-grade error handling with exponential backoff
async function executeWithRetry(
agentName,
task,
maxRetries = 3,
executionLog
) {
let lastError = null;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const result = await invokeSpecialistAgent(agentName, task);
executionLog.push({
timestamp: new Date().toISOString(),
agentName: agentName,
status: "success",
attempt: attempt + 1,
taskSummary: task.substring(0, 100),
});
return { success: true, result, totalAttempts: attempt + 1 };
} catch (error) {
lastError = error;
executionLog.push({
timestamp: new Date().toISOString(),
agentName: agentName,
status: "failed",
attempt: attempt + 1,
error: error.message,
});
if (attempt < maxRetries - 1) {
// Exponential backoff: 1s, 2s, 4s...
const backoffMs = Math.pow(2, attempt) * 1000;
await new Promise((resolve) => setTimeout(resolve, backoffMs));
}
}
}
// All retries exhausted: return fallback response
executionLog.push({
timestamp: new Date().toISOString(),
agentName: agentName,
status: "fallback_activated",
reason: `All ${maxRetries} attempts failed. Error: ${lastError.message}`,
});
return {
success: false,
fallbackResult: generateFallback(agentName, task),
totalAttempts: maxRetries,
error: lastError.message,
};
}
function generateFallback(agentName, task) {
// Fallback strategies vary by agent type.
// For ReportGenerator: return cached report from previous week
// For ContentProducer: return templated content
return {
source: "fallback",
reason: `${agentName} temporarily unavailable`,
timestamp: new Date().toISOString(),
};
}
module.exports = { executeWithRetry };Three Production-Ready Agent Services
Now let's implement three services you can actually sell.
Service 1: Weekly Report Generator
What It Does
A client connects their data sources (Google Analytics, Shopify, or Stripe). Every Friday, an agent automatically fetches the week's data, generates a professionally written report with insights, and delivers it via email and dashboard.
Implementation
// report-generator-agent.js
const Anthropic = require("@anthropic-ai/sdk");
const client = new Anthropic();
async function generateWeeklyReport(clientId, dataSource, executionLog) {
// Step 1: Fetch this week's data
const weekData = await fetchDataSourceMetrics(clientId, dataSource);
// Step 2: Generate report using Claude
const reportPrompt = `You are a professional business analyst. Write a weekly business report.
Data Summary:
- Page Views: ${weekData.pageViews}
- Conversion Rate: ${weekData.conversionRate}%
- Revenue: $${weekData.revenue}
- Top Converting Pages: ${weekData.topPages.join(", ")}
Format the report with:
1. Executive Summary (2 sentences)
2. Key Metrics (with week-over-week change)
3. Insights (what changed, why it matters)
4. Recommendations (3 specific, actionable items)
Use Markdown formatting suitable for email.`;
const response = await client.messages.create({
model: "claude-3-5-sonnet-20241022",
max_tokens: 1024,
messages: [
{
role: "user",
content: reportPrompt,
},
],
});
const reportContent = response.content[0].text;
const usage = response.usage;
// Step 3: Log for billing
executionLog.push({
timestamp: new Date().toISOString(),
clientId: clientId,
service: "WeeklyReportGenerator",
inputTokens: usage.input_tokens,
outputTokens: usage.output_tokens,
totalTokens: usage.input_tokens + usage.output_tokens,
dataSource: dataSource,
status: "completed",
});
// Step 4: Store and deliver
await storeReportInDatabase(clientId, reportContent);
await sendReportEmail(clientId, reportContent);
return {
reportId: `rpt_${Date.now()}`,
clientId: clientId,
generatedAt: new Date().toISOString(),
tokenUsage: {
input: usage.input_tokens,
output: usage.output_tokens,
total: usage.input_tokens + usage.output_tokens,
},
};
}
async function fetchDataSourceMetrics(clientId, source) {
// Placeholder: In production, use OAuth to fetch real data
if (source === "google-analytics") {
return {
pageViews: 42500,
conversionRate: 3.2,
revenue: 12750,
topPages: ["/", "/pricing", "/docs"],
};
}
return {};
}
async function storeReportInDatabase(clientId, content) {
// Store in your database
}
async function sendReportEmail(clientId, content) {
// Send via email service
}
module.exports = { generateWeeklyReport };Service 2: Data Analysis Agent
What It Does
Clients upload CSV or JSON datasets. The agent analyzes patterns, identifies customer segments, flags anomalies, and highlights growth opportunities. Results are structured and actionable.
Implementation
// data-analyzer-agent.js
async function analyzeDataset(clientId, dataUrl, analysisType, executionLog) {
// Step 1: Download and parse data
const data = await downloadAndParse(dataUrl);
// Step 2: Run analysis
const analysisPrompt = `Analyze this customer dataset with ${data.length} records.
Columns: ${Object.keys(data[0] || {}).join(", ")}
Provide:
1. Statistical Summary (totals, averages, distributions)
2. Segments (which groups of customers behave distinctly)
3. Growth Opportunities (underserved segments, low-hanging fruit)
4. Risks (declining cohorts, churn signals)
5. Top 3 Recommendations (specific, testable)
Be concrete. Avoid generalities.`;
const response = await client.messages.create({
model: "claude-3-5-sonnet-20241022",
max_tokens: 2048,
messages: [
{
role: "user",
content: analysisPrompt,
},
],
});
const analysis = response.content[0].text;
const usage = response.usage;
// Step 3: Log for billing
executionLog.push({
timestamp: new Date().toISOString(),
clientId: clientId,
service: "DataAnalyzerAgent",
inputTokens: usage.input_tokens,
outputTokens: usage.output_tokens,
totalTokens: usage.input_tokens + usage.output_tokens,
recordCount: data.length,
analysisType: analysisType,
status: "completed",
});
return {
analysisId: `ana_${Date.now()}`,
clientId: clientId,
recordsAnalyzed: data.length,
analysis: analysis,
generatedAt: new Date().toISOString(),
tokenUsage: {
input: usage.input_tokens,
output: usage.output_tokens,
total: usage.input_tokens + usage.output_tokens,
},
};
}
async function downloadAndParse(dataUrl) {
// Download and parse CSV/JSON
return [];
}
module.exports = { analyzeDataset };Service 3: Content Pipeline Agent
What It Does
Generate multiple pieces of marketing content at scale while maintaining quality. A client requests "10 blog post outlines and 20 social posts about Q2 product updates." The orchestrator spawns multiple agents, generates content, validates quality, and regenerates any pieces that don't meet standards.
Implementation
// content-pipeline-agent.js
async function executePipeline(clientId, contentBrief, executionLog) {
// Step 1: Suggest content themes
const themes = await generateContentThemes(clientId, contentBrief, executionLog);
// Step 2: Generate content for each theme
const contentPieces = [];
for (const theme of themes) {
const content = await generatePiece(
clientId,
theme,
contentBrief,
executionLog
);
contentPieces.push(content);
}
// Step 3: Quality check all pieces
const qualityChecks = contentPieces.map((piece) => ({
piece: piece,
passed: piece.length > 50 && piece.length < 500,
}));
// Step 4: Regenerate pieces that failed QC
const finalContent = [];
for (const check of qualityChecks) {
if (check.passed) {
finalContent.push(check.piece);
} else {
const regenerated = await generatePiece(
clientId,
check.piece.theme,
contentBrief + " [REGENERATE FOR LENGTH]",
executionLog
);
finalContent.push(regenerated);
}
}
return {
pipelineId: `pipe_${Date.now()}`,
clientId: clientId,
contentCount: finalContent.length,
content: finalContent,
completedAt: new Date().toISOString(),
};
}
async function generateContentThemes(clientId, brief, executionLog) {
const response = await client.messages.create({
model: "claude-3-5-sonnet-20241022",
max_tokens: 1024,
messages: [
{
role: "user",
content: `Generate 10 distinct content themes for: "${brief}". Return as JSON array.`,
},
],
});
executionLog.push({
timestamp: new Date().toISOString(),
clientId: clientId,
service: "ContentPipelineAgent",
step: "theme_generation",
inputTokens: response.usage.input_tokens,
outputTokens: response.usage.output_tokens,
totalTokens:
response.usage.input_tokens + response.usage.output_tokens,
});
return JSON.parse(response.content[0].text);
}
async function generatePiece(clientId, theme, brief, executionLog) {
const response = await client.messages.create({
model: "claude-3-5-sonnet-20241022",
max_tokens: 512,
messages: [
{
role: "user",
content: `Write social media content about: "${theme}". Style: ${brief}. Keep under 280 characters.`,
},
],
});
executionLog.push({
timestamp: new Date().toISOString(),
clientId: clientId,
service: "ContentPipelineAgent",
step: "piece_generation",
theme: theme,
inputTokens: response.usage.input_tokens,
outputTokens: response.usage.output_tokens,
totalTokens:
response.usage.input_tokens + response.usage.output_tokens,
});
return response.content[0].text;
}
module.exports = {
executePipeline,
generateContentThemes,
generatePiece,
};Usage-Based Billing: API Gateway and Token Metering
Selling agents requires precise billing. Every token consumed must be logged and attributed to the client who triggered it. Stripe's metering API handles this elegantly.
API Gateway with Token Tracking
// api-gateway.js
// Entry point for all client requests; handles auth, execution, and billing
const express = require("express");
const app = express();
const stripe = new (require("stripe"))(process.env.STRIPE_SECRET_KEY);
const executionLogs = {}; // In production: PostgreSQL
app.post("/api/v1/execute", async (req, res) => {
const { clientId, service, request } = req.body;
// Step 1: Authenticate client
const client = await authenticateClient(clientId);
if (!client || !client.subscriptionActive) {
return res.status(403).json({ error: "Subscription inactive" });
}
// Step 2: Initialize execution log
const log = [];
if (!executionLogs[clientId]) {
executionLogs[clientId] = [];
}
let result;
// Step 3: Dispatch to appropriate service
try {
if (service === "weekly-report") {
const { generateWeeklyReport } = require("./report-generator-agent");
result = await generateWeeklyReport(clientId, request.source, log);
} else if (service === "data-analysis") {
const { analyzeDataset } = require("./data-analyzer-agent");
result = await analyzeDataset(clientId, request.dataUrl, "full", log);
} else if (service === "content-pipeline") {
const { executePipeline } = require("./content-pipeline-agent");
result = await executePipeline(clientId, request.brief, log);
}
// Step 4: Calculate usage
const totalTokens = log.reduce(
(sum, entry) => sum + (entry.totalTokens || 0),
0
);
// Step 5: Report to Stripe for billing
await reportUsageToStripe(
client.stripeCustomerId,
client.subscriptionId,
totalTokens
);
// Step 6: Persist execution log
executionLogs[clientId].push(...log);
res.json({
success: true,
result: result,
usage: {
totalTokens: totalTokens,
estimatedCost: (totalTokens * 0.00001).toFixed(4),
},
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
async function reportUsageToStripe(customerId, subscriptionId, tokenCount) {
// Report token consumption to Stripe
// Stripe will bill based on your meter event pricing
console.log(`Metering: ${tokenCount} tokens for customer ${customerId}`);
// In production, call stripe.billing.meterEvents.create()
}
async function authenticateClient(clientId) {
// Look up client in your database
return { subscriptionActive: true, stripeCustomerId: "cus_123" };
}
module.exports = { app };Stripe Metering Billing Setup
// stripe-metering-setup.js
// Configure usage-based billing in Stripe
const stripe = new (require("stripe"))(process.env.STRIPE_SECRET_KEY);
async function createMeteringSubscription(customerId, planName) {
// Step 1: Create a metered price
const price = await stripe.prices.create({
currency: "usd",
product: "prod_antigravity_agents", // Your product ID
billing_scheme: "tiered",
tiers_mode: "volume",
tiers: [
{
up_to: 1000000,
unit_amount: 10, // $0.0001 per token for first 1M
},
{
up_to: "inf",
unit_amount: 8, // $0.00008 per token above 1M (volume discount)
},
],
});
// Step 2: Create subscription with metered billing
const subscription = await stripe.subscriptions.create({
customer: customerId,
items: [
{
price: price.id,
},
],
});
return {
subscriptionId: subscription.id,
priceId: price.id,
};
}
async function recordTokenConsumption(subscriptionId, tokenCount) {
// Call Stripe to record usage
// (See Stripe metering documentation for exact API)
console.log(`Recorded ${tokenCount} tokens consumed`);
}
module.exports = { createMeteringSubscription, recordTokenConsumption };Client Dashboard: What to Display
Your clients need to understand what they're being charged for. Build a dashboard showing:
Core Metrics
-
Execution Stats
- Number of runs this month (by service)
- Success rate
- Average execution time
-
Token Consumption
- Total tokens used this month
- Breakdown by service
- Projected month-end consumption
-
Billing
- Current month's estimated charge
- Usage-based costs vs. base fee
- Next billing date
-
Execution History
- Detailed logs of each run
- Success/failure status
- Token counts per run
Dashboard Data Aggregation
// dashboard-metrics.js
async function getDashboardMetrics(clientId) {
const thisMonth = new Date().getMonth();
const logs = executionLogs[clientId] || [];
const monthlyLogs = logs.filter(
(log) => new Date(log.timestamp).getMonth() === thisMonth
);
const totalTokens = monthlyLogs.reduce(
(sum, log) => sum + (log.totalTokens || 0),
0
);
const byService = {};
monthlyLogs.forEach((log) => {
const service = log.service || "unknown";
if (!byService[service]) {
byService[service] = { count: 0, tokens: 0 };
}
byService[service].count++;
byService[service].tokens += log.totalTokens || 0;
});
return {
totalExecutions: monthlyLogs.length,
totalTokens: totalTokens,
estimatedCharge: (totalTokens * 0.00001).toFixed(2),
executionsByService: byService,
recentRuns: monthlyLogs.slice(-20),
};
}
module.exports = { getDashboardMetrics };Pricing Models: Theory to Practice
Pricing agent services requires balancing three cost streams: API costs, infrastructure, and profit margin.
Three-Tier Pricing Structure
| Service | Setup Fee | Monthly Base | Usage Fee | Target Market | | --- | --- | --- | --- | --- | | Weekly Report | $500 | $299/mo | $0.10/report (after 20 free) | Early-stage startups | | Data Analysis | $1,000 | $499/mo | $0.05 per 1M tokens | SMB to mid-market | | Content Pipeline | $2,000 | $999/mo | $0.001/piece unlimited | Enterprise, agencies |
The Math Behind Monthly Pricing
For Weekly Report Generator at $299/month:
- Anthropic API cost: ~$50/month (assuming 20 reports × $2.50 per report)
- Infrastructure & ops: ~$50/month
- Support & SLA overhead: ~$50/month
- Gross profit: ~$149/month
That's 50% margin on month-to-month costs, which funds product development and customer acquisition.
For usage beyond the free tier ($0.10/report):
- Your cost: ~$0.08/report
- Your margin: $0.02/report (20%)
This structure makes the base fee worthwhile for customers (20 free reports) while giving them flexibility to scale usage affordably.
Critical Implementation Considerations
1. Define Your SLA
Be specific: "99.5% uptime measured monthly" or "less than 30 minutes MTTR for outages." Don't promise 99.99% if you can't deliver it.
2. Handle Data Privacy
If you're processing client data (customer lists, analytics, financial data), ensure compliance with GDPR, CCPA, SOC 2, or industry-specific regulations.
3. Start Small, Scale Carefully
Launch with a single client or small beta cohort. Verify infrastructure stability and support workflows before offering to dozens of clients.
4. Build Support Runbooks
Document: What do we do if a client's report fails to generate? How quickly do we refund? Who investigates? Create clear escalation paths.
The Agent Economy Begins Now
The gap between "I automated my workflow" and "I sell automation to others" is the gap between a tool and a business. Filling that gap requires reliability, transparency, and precise billing—three things we've implemented throughout this article.
Antigravity's AgentKit 2.0 gives you the orchestration primitives. Combined with proper error handling, token tracking, and Stripe metering, you have everything you need to launch agent-powered services.
If you're currently spending 20 hours per month on work that agents could handle, you've found your first product. Use the patterns from this article—orchestration, resilience, usage logging, and SaaS billing—to transform that time savings into a revenue stream.
The agent economy is here. Will you sell into it?