# Client Manager

> 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".

- Skill: `cleanexpo/client-manager` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add cleanexpo/client-manager`
- Raw SKILL.md: https://api.skillmd.com/api/skills/cleanexpo/client-manager/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Marketing & Growth
- Author: cleanexpo (https://skillmd.com/u/cleanexpo)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/cleanexpo/client-manager

---


# Client & Campaign Data Manager

## Process

1. **Identify operation** -- determine the CRUD action and target model from user intent
2. **Validate inputs** -- check required fields, enum values, and foreign key references
3. **Execute via Prisma** -- run the appropriate Prisma Client query against PostgreSQL
4. **Return structured result** -- format the response with relevant fields and relations
5. **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.

