Skip to main content

Services

The API uses a service layer pattern. Route handlers delegate to services, which contain all business logic. Services are the single source of truth for data operations -- routes never call Prisma directly.


Service Location

All services are in apps/api/src/services/. There are 57 service files organized by domain.


Core Services

ServiceResponsibilityKey Functions
auth.serviceAuthentication, JWT issuance, OAuth flows, OTPregister(), login(), refreshToken(), oauthCallback()
account.serviceUser account managementgetProfile(), updateProfile(), deleteAccount()
workspace.serviceWorkspace CRUD, member management, invitationscreate(), addMember(), updateRole(), removeMember()
notification.servicePush and in-app notificationssend(), markRead(), getUnread()
audit-log.serviceAudit trail recordinglog(), query()

Content Services

ServiceResponsibilityKey Functions
post.servicePost CRUD, publishing pipelinecreate(), publish(), duplicate(), getAll()
scheduler.serviceSchedule management, trigger publishingschedule(), cancel(), reschedule()
media.serviceFile upload to S3, storage, optimizationupload(), delete(), getUrl()
brand-voice.serviceBrand voice training and retrievaltrain(), apply(), getConfig()
content-calendar.serviceAI calendar planning and generationgenerate(), getEntries()
content-repurpose.serviceContent transformation across formatsrepurpose()

AI Services

ServiceResponsibilityKey Functions
ai.serviceCore Gemini API integrationcallGemini(), generateCaption(), rewriteCaption(), generateHashtags(), generateCTA(), translateCaption(), generateImage()
ai-advisor.servicePerformance recommendationsgetRecommendations()
trend-scanner.serviceTrend detection and analysisscan(), getTrends()
classification.servicePost content type classificationclassify(), getClassification()
embedding.serviceVector embeddings (text-embedding-004)generate(), search(), similarity()
AI Usage Tracking

All AI service calls go through incrementUsage() which tracks per-workspace consumption against plan quotas. Usage data is stored in the WorkspaceUsage model and accessible via the admin dashboard.


Conversation Engine Services (ICE)

ServiceResponsibilityKey Functions
conversation-engine.serviceOrchestrates the 3-step LLM pipelineprocessIncomingMessage(), listThreads(), getThread(), getThreadMessages(), sendAgentReply(), resolveThread(), reopenThread()
conversation-brain.serviceLLM response generation via GeminigenerateReply(), suggestReply()
conversation-memory.service3-tier memory system (short/medium/long term)getContext(), store(), update()
intent-classifier.serviceMessage intent and sentiment detectionclassify()
escalation.serviceEscalation rule evaluation and routingevaluate(), escalate()
context-builder.serviceBuilds full context for AI responsesbuild()
bot-config.serviceBot toggle and configurationtoggleThreadBot(), getBotConfig(), updateBotConfig(), getInboxStats()

Analytics Services

ServiceResponsibilityKey Functions
analytics.serviceMetrics aggregation, dashboard, time-seriesgetDashboard(), getTimeSeries(), getByPlatform(), getPostAnalytics()
ab-test.serviceA/B test CRUD and evaluationcreate(), evaluate(), getResults()
prediction.serviceEngagement and revenue predictionspredict(), getAccuracy()
benchmark.serviceIndustry and niche benchmarkingcompute(), compare()

Integration Services

ServiceResponsibilityKey Functions
ecommerce.serviceStore sync (Shopify/WooCommerce/EasyOrders)connect(), syncProducts(), syncOrders()
commerce-order.serviceOrder tracking, revenue attributiongetOrders(), attributeRevenue()
ads.serviceMeta Ads, TikTok Promote, auto-boostcreateCampaign(), boost(), syncMetrics()
competitor.serviceCompetitor data managementadd(), getSnapshots(), getReport()
competitor-scraper.serviceCompetitor metrics scrapingscrape()
payment.serviceStripe/Paymob processing, webhookscreateSession(), handleWebhook()
integration.serviceThird-party integrationsconnect(), disconnect(), sync()

Workflow Services

ServiceResponsibilityKey Functions
workflow-engine.serviceWorkflow execution engineemitWorkflowEvent(), triggerTestRun(), executeWorkflow(), evaluateCondition(), executeAction()

The workflow engine supports these node types and actions:

Node TypePurpose
triggerEvent that starts the workflow
conditionEvaluates a boolean expression
actionPerforms an operation
Condition OperatorDescription
eqEqual to
neqNot equal to
gt / gteGreater than / greater than or equal
lt / lteLess than / less than or equal
containsString contains
not_containsString does not contain
Action TypeDescription
send_notificationSend a notification to workspace members
create_draftCreate a draft post
publish_postPublish a post immediately
send_replySend a conversation reply
waitDelay execution for a duration
boost_postAuto-boost a post via ads
ai_repurpose_contentRepurpose content using AI

Other Services

ServiceResponsibilityKey Functions
audience.serviceAudience graph, segmentation, tagginggetNodes(), getSegments(), tag()
approval.serviceApproval request workflowsrequest(), approve(), reject()
client-report.serviceClient report generationgenerate(), getReports()

Writing a New Service

Follow this pattern for consistency:

// apps/api/src/services/example.service.ts
import { prisma } from '../lib/prisma';
import type { CreateExampleInput } from '@unipulse/shared';

export const exampleService = {
async getAll(workspaceId: string) {
return prisma.example.findMany({
where: { workspaceId },
orderBy: { createdAt: 'desc' },
});
},

async getById(workspaceId: string, id: string) {
const item = await prisma.example.findFirst({
where: { id, workspaceId },
});
if (!item) throw new AppError('NOT_FOUND', 'Example not found');
return item;
},

async create(workspaceId: string, data: CreateExampleInput) {
return prisma.example.create({
data: { ...data, workspaceId },
});
},

async update(workspaceId: string, id: string, data: Partial<CreateExampleInput>) {
return prisma.example.update({
where: { id, workspaceId },
data,
});
},

async delete(workspaceId: string, id: string) {
return prisma.example.delete({
where: { id, workspaceId },
});
},
};
Always Scope by Workspace

Every query must include workspaceId to maintain multi-tenant isolation. Never return data from other workspaces.


Cross-Reference