Building Services
Step-by-step guide to implementing services from scratch
Learn how to build services from scratch with step-by-step examples and best practices.
Quick Start: Your First Service
Let's build a simple email summarization service that takes long emails and creates concise summaries. For a complete walkthrough, see Quick Start Guide →
Step 1: Define the Service
Start with a clear service definition. Learn more about Service Definition →
import $, { ai } from 'sdk.do'
const emailSummaryService = await $.Service.create({
// Basic information
name: 'Email Summarizer',
description: 'Condense long emails into brief, actionable summaries',
version: '1.0.0',
// Classification
type: $.ServiceType.ContentGeneration,
category: $.ServiceCategory.Productivity,
// What it needs
input: {
required: ['emailContent'],
optional: ['maxLength', 'style'],
},
// What it provides
output: {
summary: 'string',
keyPoints: 'array',
actionItems: 'array',
sentiment: 'string',
},
// Service guarantees
sla: {
responseTime: '10 seconds',
accuracy: 0.9,
},
// Pricing
pricing: {
model: 'per-summary',
rate: 0.1,
},
})Step 2: Implement the Logic
Create the service handler:
import { on, send } from 'sdk.do'
on.ServiceRequest.created(async (request) => {
// Only handle our service
if (request.serviceId !== emailSummaryService.id) return
try {
const { emailContent, maxLength, style } = request.inputs
// Generate summary using AI
const result = await ai.generate({
model: 'gpt-5',
systemPrompt: 'You are an expert at summarizing emails concisely.',
prompt: `Summarize this email in ${maxLength || 150} words or less:
${emailContent}
Provide:
1. A brief summary
2. Key points (bullet list)
3. Action items (if any)
4. Overall sentiment`,
temperature: 0.3, // Lower temperature for consistency
})
// Parse the AI response
const parsed = parseEmailSummary(result.content)
// Deliver the result
send.ServiceResult.deliver({
requestId: request.id,
outputs: {
summary: parsed.summary,
keyPoints: parsed.keyPoints,
actionItems: parsed.actionItems,
sentiment: parsed.sentiment,
},
metadata: {
originalLength: emailContent.length,
summaryLength: parsed.summary.length,
compressionRatio: parsed.summary.length / emailContent.length,
},
})
// Charge for the service
send.Payment.charge({
customerId: request.customerId,
amount: emailSummaryService.pricing.rate,
description: 'Email Summary Service',
})
} catch (error) {
// Handle errors gracefully
send.ServiceRequest.fail({
requestId: request.id,
error: error.message,
retryable: error.retryable || false,
})
}
})
// Helper function to parse AI output
function parseEmailSummary(content: string) {
// Extract structured data from AI response
const lines = content.split('\n').filter((l) => l.trim())
return {
summary: extractSummary(lines),
keyPoints: extractKeyPoints(lines),
actionItems: extractActionItems(lines),
sentiment: extractSentiment(lines),
}
}Step 3: Test the Service
Create comprehensive tests. Learn more about Quality Assurance →
import { test, expect } from 'vitest'
test('summarizes email correctly', async () => {
const testEmail = `
Dear Team,
I wanted to follow up on our discussion from yesterday's meeting about the Q4 roadmap.
As agreed, we need to prioritize the following items:
1. Complete the API redesign by end of October
2. Launch the mobile app beta in November
3. Prepare marketing materials for December launch
Please review the attached document and send me your feedback by Friday.
We'll need to make some tough decisions about scope if we want to hit these dates.
Also, don't forget about the team offsite next month - I'll send calendar invites soon.
Best regards,
John
`
const result = await send.ServiceRequest.create({
serviceId: emailSummaryService.id,
inputs: {
emailContent: testEmail,
maxLength: 100,
},
})
// Wait for result
const output = await waitForResult(result.id)
// Verify outputs
expect(output.summary).toBeDefined()
expect(output.summary.length).toBeLessThanOrEqual(100)
expect(output.keyPoints).toHaveLength(3)
expect(output.actionItems).toContain('Review document by Friday')
expect(output.sentiment).toBe('professional')
})
test('handles error cases', async () => {
const result = await send.ServiceRequest.create({
serviceId: emailSummaryService.id,
inputs: {
emailContent: '', // Empty email
},
})
const output = await waitForResult(result.id)
expect(output.error).toBeDefined()
})Step 4: Deploy the Service
Make your service available. Learn more about Deployment →
// Deploy to production
await $.Service.deploy({
serviceId: emailSummaryService.id,
environment: 'production',
configuration: {
maxConcurrency: 50,
timeout: 30000, // 30 seconds
retryPolicy: {
maxRetries: 3,
backoff: 'exponential',
},
},
})
// Make service discoverable
await $.Service.publish({
serviceId: emailSummaryService.id,
visibility: 'public',
categories: ['productivity', 'communication'],
tags: ['email', 'summary', 'ai'],
})Building a More Complex Service
Let's build a comprehensive content marketing service that combines multiple capabilities.
Service Architecture
import $, { ai, db, on, send } from 'sdk.do'
const contentMarketingService = await $.Service.create({
name: 'Content Marketing Suite',
type: $.ServiceType.Composite,
description: 'Complete content marketing from research to publication',
// Define workflow stages
workflow: [
{
stage: 'research',
name: 'Topic Research',
required: true,
estimated: '2 minutes',
},
{
stage: 'outline',
name: 'Content Outline',
required: true,
estimated: '1 minute',
dependsOn: ['research'],
},
{
stage: 'content',
name: 'Content Creation',
required: true,
estimated: '5 minutes',
dependsOn: ['outline'],
},
{
stage: 'seo',
name: 'SEO Optimization',
required: true,
estimated: '2 minutes',
dependsOn: ['content'],
},
{
stage: 'images',
name: 'Image Generation',
required: false,
estimated: '3 minutes',
dependsOn: ['content'],
},
{
stage: 'social',
name: 'Social Media Posts',
required: false,
estimated: '2 minutes',
dependsOn: ['content'],
},
],
// Input requirements
input: {
required: ['topic', 'targetKeywords', 'targetAudience'],
optional: ['tone', 'length', 'includeImages', 'generateSocial'],
},
// Pricing
pricing: {
model: 'bundled',
baseRate: 100.0,
options: {
images: 25.0,
socialPosts: 15.0,
},
},
})Implementing the Workflow
on.ServiceRequest.created(async (request) => {
if (request.serviceId !== contentMarketingService.id) return
const results = {}
const inputs = request.inputs
try {
// Stage 1: Research
await updateProgress(request.id, 'research', 'in-progress')
const research = await ai.research({
model: 'gpt-5',
topic: inputs.topic,
keywords: inputs.targetKeywords,
sources: ['web', 'news', 'academic'],
depth: 'comprehensive',
})
results.research = research
await updateProgress(request.id, 'research', 'completed')
// Stage 2: Create Outline
await updateProgress(request.id, 'outline', 'in-progress')
const outline = await ai.generate({
model: 'gpt-5',
type: 'content-outline',
context: {
topic: inputs.topic,
research: research.summary,
keywords: inputs.targetKeywords,
audience: inputs.targetAudience,
},
})
results.outline = outline
await updateProgress(request.id, 'outline', 'completed')
// Stage 3: Generate Content
await updateProgress(request.id, 'content', 'in-progress')
const content = await ai.generate({
model: 'gpt-5',
type: 'blog-post',
context: {
outline: outline.content,
research: research.findings,
tone: inputs.tone || 'professional',
length: inputs.length || 1500,
keywords: inputs.targetKeywords,
},
quality: {
minReadability: 60,
keywordDensity: { min: 0.01, max: 0.03 },
},
})
results.content = content
await updateProgress(request.id, 'content', 'completed')
// Stage 4: SEO Optimization
await updateProgress(request.id, 'seo', 'in-progress')
const seo = await ai.optimize({
model: 'gpt-5',
content: content.text,
keywords: inputs.targetKeywords,
optimizations: ['meta-description', 'title-tag', 'header-structure', 'internal-linking', 'image-alt-text'],
})
results.seo = seo
await updateProgress(request.id, 'seo', 'completed')
// Stage 5: Generate Images (optional)
if (inputs.includeImages) {
await updateProgress(request.id, 'images', 'in-progress')
const images = await ai.generateImages({
model: 'dalle-3',
prompts: await ai.createImagePrompts({
content: content.text,
count: 3,
style: 'professional',
}),
})
results.images = images
await updateProgress(request.id, 'images', 'completed')
}
// Stage 6: Create Social Posts (optional)
if (inputs.generateSocial) {
await updateProgress(request.id, 'social', 'in-progress')
const socialPosts = await ai.generate({
model: 'gpt-5',
type: 'social-media-posts',
context: {
content: content.summary,
platforms: ['twitter', 'linkedin', 'facebook'],
tone: inputs.tone,
},
})
results.socialPosts = socialPosts
await updateProgress(request.id, 'social', 'completed')
}
// Deliver complete package
send.ServiceResult.deliver({
requestId: request.id,
outputs: results,
metadata: {
stages: contentMarketingService.workflow.map((s) => s.stage),
totalTime: calculateTotalTime(results),
quality: assessQuality(results),
},
})
// Calculate final billing
let cost = contentMarketingService.pricing.baseRate
if (inputs.includeImages) cost += contentMarketingService.pricing.options.images
if (inputs.generateSocial) cost += contentMarketingService.pricing.options.socialPosts
send.Payment.charge({
customerId: request.customerId,
amount: cost,
description: 'Content Marketing Suite',
breakdown: {
base: contentMarketingService.pricing.baseRate,
images: inputs.includeImages ? contentMarketingService.pricing.options.images : 0,
social: inputs.generateSocial ? contentMarketingService.pricing.options.socialPosts : 0,
},
})
} catch (error) {
send.ServiceRequest.fail({
requestId: request.id,
error: error.message,
stage: getCurrentStage(results),
partialResults: results,
})
}
})
// Helper: Update progress
async function updateProgress(requestId: string, stage: string, status: string) {
await db.ServiceRequest.update({
where: { id: requestId },
data: {
progress: {
[stage]: {
status,
timestamp: new Date(),
},
},
},
})
send.ServiceProgress.updated({
requestId,
stage,
status,
})
}Service Patterns
Pattern 1: Synchronous Services
For quick, immediate responses:
// Simple validation service
const validationService = await $.Service.create({
name: 'Email Validator',
type: $.ServiceType.Validation,
executionMode: 'synchronous',
pricing: {
model: 'per-validation',
rate: 0.01,
},
})
on.ServiceRequest.created(async (request) => {
if (request.serviceId !== validationService.id) return
// Validate immediately
const isValid = await validateEmail(request.inputs.email)
// Return result synchronously
send.ServiceResult.deliver({
requestId: request.id,
outputs: {
valid: isValid,
details: isValid ? null : getValidationErrors(request.inputs.email),
},
})
})Pattern 2: Asynchronous Services
For long-running operations:
// Video transcription service
const transcriptionService = await $.Service.create({
name: 'Video Transcriber',
type: $.ServiceType.MediaProcessing,
executionMode: 'asynchronous',
pricing: {
model: 'per-minute',
rate: 0.5,
},
})
on.ServiceRequest.created(async (request) => {
if (request.serviceId !== transcriptionService.id) return
// Start async processing
send.ServiceExecution.start({
requestId: request.id,
estimatedDuration: estimateTranscriptionTime(request.inputs.videoUrl),
})
// Process in background
processVideoTranscription(request).then(async (result) => {
send.ServiceResult.deliver({
requestId: request.id,
outputs: result,
})
})
// Return immediately with job ID
send.ServiceRequest.acknowledged({
requestId: request.id,
message: 'Transcription started, you will be notified when complete',
})
})Pattern 3: Streaming Services
For real-time results:
// Real-time translation service
const translationService = await $.Service.create({
name: 'Live Translator',
type: $.ServiceType.Translation,
executionMode: 'streaming',
pricing: {
model: 'per-session',
rate: 5.0,
},
})
on.ServiceRequest.created(async (request) => {
if (request.serviceId !== translationService.id) return
// Create streaming session
const session = await createStreamingSession(request)
// Stream translations as they arrive
for await (const chunk of session.inputStream) {
const translated = await ai.translate({
text: chunk,
from: request.inputs.sourceLanguage,
to: request.inputs.targetLanguage,
})
send.ServiceResult.stream({
requestId: request.id,
chunk: translated,
})
}
// Close session
send.ServiceResult.complete({
requestId: request.id,
})
})Pattern 4: Human-in-the-Loop Services
For services requiring human review. See complete workflow example →
// Content moderation service
const moderationService = await $.Service.create({
name: 'Content Moderator',
type: $.ServiceType.ContentModeration,
executionMode: 'human-in-loop',
pricing: {
model: 'tiered',
ai: 0.05,
human: 1.0,
},
})
on.ServiceRequest.created(async (request) => {
if (request.serviceId !== moderationService.id) return
// AI first pass
const aiModeration = await ai.moderate({
model: 'gpt-5',
content: request.inputs.content,
policies: request.inputs.policies,
})
// If confidence is high, auto-approve/reject
if (aiModeration.confidence > 0.95) {
send.ServiceResult.deliver({
requestId: request.id,
outputs: {
decision: aiModeration.decision,
reason: aiModeration.reason,
reviewedBy: 'ai',
},
})
send.Payment.charge({
customerId: request.customerId,
amount: moderationService.pricing.ai,
})
return
}
// Low confidence - send to human
send.HumanReview.create({
requestId: request.id,
content: request.inputs.content,
aiSuggestion: aiModeration,
priority: aiModeration.severity,
})
send.Payment.charge({
customerId: request.customerId,
amount: moderationService.pricing.human,
})
})
// Human review complete
on.HumanReview.completed(async (review) => {
send.ServiceResult.deliver({
requestId: review.requestId,
outputs: {
decision: review.decision,
reason: review.reason,
reviewedBy: 'human',
reviewer: review.reviewerId,
},
})
})Best Practices
1. Error Handling
Always handle errors gracefully:
try {
// Service execution
} catch (error) {
// Log error
db.ErrorLog.create({
serviceId: service.id,
requestId: request.id,
error: error.message,
stack: error.stack,
timestamp: new Date(),
})
// Notify customer
send.ServiceRequest.fail({
requestId: request.id,
error: error.message,
retryable: isRetryableError(error),
})
// Alert team if critical
if (error.severity === 'critical') {
send.Alert.create({
type: 'service-failure',
serviceId: service.id,
error: error.message,
})
}
}2. Input Validation
Validate all inputs before processing:
function validateServiceInputs(request, service) {
const errors = []
// Check required fields
for (const field of service.input.required) {
if (!request.inputs[field]) {
errors.push(`Missing required field: ${field}`)
}
}
// Validate field types
for (const [field, value] of Object.entries(request.inputs)) {
const expectedType = service.input.schema[field]
if (!validateType(value, expectedType)) {
errors.push(`Invalid type for ${field}: expected ${expectedType}`)
}
}
// Business rule validation
if (service.input.rules) {
for (const rule of service.input.rules) {
if (!rule.validate(request.inputs)) {
errors.push(rule.message)
}
}
}
return {
valid: errors.length === 0,
errors,
}
}3. Progress Tracking
Keep customers informed:
// Start
send.ServiceProgress.started({
requestId: request.id,
estimatedDuration: service.sla.responseTime,
})
// Updates
send.ServiceProgress.updated({
requestId: request.id,
stage: 'processing',
progress: 0.5, // 50%
})
// Complete
send.ServiceProgress.completed({
requestId: request.id,
duration: actualDuration,
})4. Quality Assurance
Verify output quality:
async function verifyQuality(result, service) {
// Check against SLA
if (result.duration > service.sla.responseTime) {
send.Alert.create({
type: 'sla-breach',
metric: 'response-time',
})
}
// Validate output
if (service.output.validation) {
const validation = await service.output.validation(result.outputs)
if (!validation.valid) {
throw new Error(`Quality check failed: ${validation.errors}`)
}
}
// AI quality assessment
if (service.quality.aiCheck) {
const assessment = await ai.assess({
output: result.outputs,
criteria: service.quality.criteria,
})
if (assessment.score < service.quality.threshold) {
// Regenerate or flag for review
}
}
}Next Steps
- Quick Start Guide → - Build your first service in 5 minutes
- Service Definition Guide → - Define service capabilities
- Pricing & Billing Guide → - Implement monetization
- Quality Assurance Guide → - Testing strategies
- Learn Service Composition → - Combine services
- Deploy Services → - Go to production
- Explore Examples → - See real implementations