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