Client & Campaign Data Manager
Process
- Identify operation -- determine the CRUD action and target model from user intent
- Validate inputs -- check required fields, enum values, and foreign key references
- Execute via Prisma -- run the appropriate Prisma Client query against PostgreSQL
- Return structured result -- format the response with relevant fields and relations
- Log API usage -- record the operation in ApiUsage for audit trail
Prisma Models
User
id (cuid), email (unique), name, password
- OAuth:
googleId, avatar, authProvider (local | google)
- API keys:
openrouterApiKey, anthropicApiKey (encrypted)
- Settings:
preferences (JSON)
- Relations:
campaigns[], projects[], apiUsage[]
- Table:
users
Campaign
id (cuid), name, description, platform, status
- Platform values: youtube, instagram, tiktok, twitter, facebook, linkedin, pinterest, reddit
- Status values: draft, active, paused, completed
- Data fields:
content (JSON), analytics (JSON), settings (JSON)
- Relations: belongs to
User, has many Post[]
- Table:
campaigns
Post
id (cuid), content, platform, status
- Status values: draft, scheduled, published, failed
- Scheduling:
scheduledAt, publishedAt
- Data fields:
metadata (JSON -- images, hashtags, mentions), analytics (JSON)
- Relations: belongs to
Campaign
- Table:
posts
Project
id (cuid), name, description, type, data (JSON)
- Type values: marketing, content, analytics
- Relations: belongs to
User
- Table:
projects
ApiUsage
id (cuid), endpoint, model, tokens, cost, status
- Status values: success, error, rate_limited
- Data fields:
requestData (JSON), responseData (JSON), errorMessage
- Relations: belongs to
User
- Table:
api_usage
Session
id (cuid), token (unique), userId, expiresAt
- Table:
sessions
Operations
Campaign CRUD
| Operation |
Prisma Method |
Required Fields |
| CREATE |
prisma.campaign.create() |
name, platform, userId |
| READ |
prisma.campaign.findUnique() / findMany() |
id or filter |
| UPDATE |
prisma.campaign.update() |
id, fields to update |
| DELETE |
prisma.campaign.delete() |
id |
| LIST |
prisma.campaign.findMany() |
userId, optional status/platform filter |
Post Management
| Operation |
Prisma Method |
Required Fields |
| CREATE |
prisma.post.create() |
content, platform, campaignId |
| SCHEDULE |
prisma.post.update() |
id, scheduledAt, status = "scheduled" |
| PUBLISH |
prisma.post.update() |
id, publishedAt, status = "published" |
| BULK CREATE |
prisma.post.createMany() |
array of post data |
Analytics Tracking
- Store per-post engagement in
Post.analytics JSON field
- Store campaign-level rollups in
Campaign.analytics JSON field
- Track API costs via
ApiUsage model per request
- Aggregate by: platform, date range, campaign, user
User Preferences
- Stored in
User.preferences JSON field
- Includes: default platform, timezone, notification settings, brand voice
- Update via
prisma.user.update({ data: { preferences: {...} } })
Query Patterns
Filtering campaigns by status and platform
prisma.campaign.findMany({
where: { userId, status, platform },
include: { posts: true },
orderBy: { updatedAt: 'desc' }
})
Aggregating post analytics across a campaign
prisma.post.findMany({
where: { campaignId },
select: { analytics: true, platform: true, status: true }
})
Tracking API costs for a user over a date range
prisma.apiUsage.aggregate({
where: { userId, createdAt: { gte: startDate, lte: endDate } },
_sum: { cost: true, tokens: true }
})
Output
- Single record: full model object with included relations
- List: array of records with pagination metadata (total, page, pageSize)
- Mutation: updated record confirming the change
- Analytics: aggregated metrics with breakdowns by platform and date range
- Errors: structured error with code, message, and affected field
Validation Rules
- Campaign
name must be non-empty and under 255 characters
- Campaign
platform must be one of the 8 supported platforms
- Post
content must be non-empty
- Post
scheduledAt must be in the future for scheduling operations
- User
email must be unique and valid format
- All delete operations cascade to child records (posts under campaigns, etc.)
Reference skill: This is a read-only architecture guide — it documents existing systems and does not generate creative or code output. No capability uplift block is needed.
Foundation & Gate Wiring (SYN-1049)
Adopted from the senior-skill standard so every artefact this connector produces is checked against the locked foundation before it lands.
Reads at every invocation (never cached — re-read each run):
.claude/memory/ceo-foundation.md — cross-client boundary (Phase 3.4), org-scoping.
.claude/memory/verification-gates.md — gate state for any claim referenced.
Output gate: every output passes the verification gate (.claude/rules/verification-gate.md) before being reported complete — run the real command/check and report actual results, never "should work".
Evidence standard: every quantitative or factual claim carries exactly one tag — [VERIFIED] / [INFERENCE] / [UNCONFIRMED]. Untagged = defect (.claude/rules/fabel-evidence-standard.md). Never state a projected result as fact.
Spec: see spec.md in this skill directory.
1---2name: client-manager3description: Campaign and client data management via Prisma ORM. Handles CRUD operations on campaigns, posts, projects, and user preferences. Tracks analytics and API usage. Use when user says "create campaign", "list campaigns", "update post", "delete project", "client data", or "manage analytics".4---56# Client & Campaign Data Manager78## Process9101. **Identify operation** -- determine the CRUD action and target model from user intent112. **Validate inputs** -- check required fields, enum values, and foreign key references123. **Execute via Prisma** -- run the appropriate Prisma Client query against PostgreSQL134. **Return structured result** -- format the response with relevant fields and relations145. **Log API usage** -- record the operation in ApiUsage for audit trail1516## Prisma Models1718### User1920- `id` (cuid), `email` (unique), `name`, `password`21- OAuth: `googleId`, `avatar`, `authProvider` (local | google)22- API keys: `openrouterApiKey`, `anthropicApiKey` (encrypted)23- Settings: `preferences` (JSON)24- Relations: `campaigns[]`, `projects[]`, `apiUsage[]`25- Table: `users`2627### Campaign2829- `id` (cuid), `name`, `description`, `platform`, `status`30- Platform values: youtube, instagram, tiktok, twitter, facebook, linkedin, pinterest, reddit31- Status values: draft, active, paused, completed32- Data fields: `content` (JSON), `analytics` (JSON), `settings` (JSON)33- Relations: belongs to `User`, has many `Post[]`34- Table: `campaigns`3536### Post3738- `id` (cuid), `content`, `platform`, `status`39- Status values: draft, scheduled, published, failed40- Scheduling: `scheduledAt`, `publishedAt`41- Data fields: `metadata` (JSON -- images, hashtags, mentions), `analytics` (JSON)42- Relations: belongs to `Campaign`43- Table: `posts`4445### Project4647- `id` (cuid), `name`, `description`, `type`, `data` (JSON)48- Type values: marketing, content, analytics49- Relations: belongs to `User`50- Table: `projects`5152### ApiUsage5354- `id` (cuid), `endpoint`, `model`, `tokens`, `cost`, `status`55- Status values: success, error, rate_limited56- Data fields: `requestData` (JSON), `responseData` (JSON), `errorMessage`57- Relations: belongs to `User`58- Table: `api_usage`5960### Session6162- `id` (cuid), `token` (unique), `userId`, `expiresAt`63- Table: `sessions`6465## Operations6667### Campaign CRUD6869| Operation | Prisma Method | Required Fields |70| --------- | --------------------------------------------- | --------------------------------------- |71| CREATE | `prisma.campaign.create()` | name, platform, userId |72| READ | `prisma.campaign.findUnique()` / `findMany()` | id or filter |73| UPDATE | `prisma.campaign.update()` | id, fields to update |74| DELETE | `prisma.campaign.delete()` | id |75| LIST | `prisma.campaign.findMany()` | userId, optional status/platform filter |7677### Post Management7879| Operation | Prisma Method | Required Fields |80| ----------- | -------------------------- | ------------------------------------- |81| CREATE | `prisma.post.create()` | content, platform, campaignId |82| SCHEDULE | `prisma.post.update()` | id, scheduledAt, status = "scheduled" |83| PUBLISH | `prisma.post.update()` | id, publishedAt, status = "published" |84| BULK CREATE | `prisma.post.createMany()` | array of post data |8586### Analytics Tracking8788- Store per-post engagement in `Post.analytics` JSON field89- Store campaign-level rollups in `Campaign.analytics` JSON field90- Track API costs via `ApiUsage` model per request91- Aggregate by: platform, date range, campaign, user9293### User Preferences9495- Stored in `User.preferences` JSON field96- Includes: default platform, timezone, notification settings, brand voice97- Update via `prisma.user.update({ data: { preferences: {...} } })`9899## Query Patterns100101### Filtering campaigns by status and platform102103```104prisma.campaign.findMany({105 where: { userId, status, platform },106 include: { posts: true },107 orderBy: { updatedAt: 'desc' }108})109```110111### Aggregating post analytics across a campaign112113```114prisma.post.findMany({115 where: { campaignId },116 select: { analytics: true, platform: true, status: true }117})118```119120### Tracking API costs for a user over a date range121122```123prisma.apiUsage.aggregate({124 where: { userId, createdAt: { gte: startDate, lte: endDate } },125 _sum: { cost: true, tokens: true }126})127```128129## Output130131- **Single record**: full model object with included relations132- **List**: array of records with pagination metadata (total, page, pageSize)133- **Mutation**: updated record confirming the change134- **Analytics**: aggregated metrics with breakdowns by platform and date range135- **Errors**: structured error with code, message, and affected field136137## Validation Rules138139- Campaign `name` must be non-empty and under 255 characters140- Campaign `platform` must be one of the 8 supported platforms141- Post `content` must be non-empty142- Post `scheduledAt` must be in the future for scheduling operations143- User `email` must be unique and valid format144- All delete operations cascade to child records (posts under campaigns, etc.)145146> **Reference skill:** This is a read-only architecture guide — it documents existing systems and does not generate creative or code output. No capability uplift block is needed.147148---149150## Foundation & Gate Wiring (SYN-1049)151152> Adopted from the senior-skill standard so every artefact this connector produces is checked against the locked foundation before it lands.153154**Reads at every invocation (never cached — re-read each run):**155156- `.claude/memory/ceo-foundation.md` — cross-client boundary (Phase 3.4), org-scoping.157- `.claude/memory/verification-gates.md` — gate state for any claim referenced.158159**Output gate:** every output passes the verification gate (`.claude/rules/verification-gate.md`) before being reported complete — run the real command/check and report actual results, never "should work".160161**Evidence standard:** every quantitative or factual claim carries exactly one tag — `[VERIFIED]` / `[INFERENCE]` / `[UNCONFIRMED]`. Untagged = defect (`.claude/rules/fabel-evidence-standard.md`). Never state a projected result as fact.162163**Spec:** see `spec.md` in this skill directory.