@growthub/cli 0.3.55 → 0.3.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +56 -156
  2. package/assets/worker-kits/growthub-zernio-social-v1/QUICKSTART.md +209 -0
  3. package/assets/worker-kits/growthub-zernio-social-v1/brands/NEW-CLIENT.md +74 -0
  4. package/assets/worker-kits/growthub-zernio-social-v1/brands/_template/brand-kit.md +131 -0
  5. package/assets/worker-kits/growthub-zernio-social-v1/brands/growthub/brand-kit.md +141 -0
  6. package/assets/worker-kits/growthub-zernio-social-v1/bundles/growthub-zernio-social-v1.json +55 -0
  7. package/assets/worker-kits/growthub-zernio-social-v1/docs/ai-caption-layer.md +132 -0
  8. package/assets/worker-kits/growthub-zernio-social-v1/docs/local-adapters.md +123 -0
  9. package/assets/worker-kits/growthub-zernio-social-v1/docs/platform-coverage.md +112 -0
  10. package/assets/worker-kits/growthub-zernio-social-v1/docs/postiz-ui-shell-integration.md +166 -0
  11. package/assets/worker-kits/growthub-zernio-social-v1/docs/posts-and-queues-layer.md +208 -0
  12. package/assets/worker-kits/growthub-zernio-social-v1/docs/zernio-api-integration.md +265 -0
  13. package/assets/worker-kits/growthub-zernio-social-v1/examples/analytics-brief-sample.md +97 -0
  14. package/assets/worker-kits/growthub-zernio-social-v1/examples/client-proposal-sample.md +106 -0
  15. package/assets/worker-kits/growthub-zernio-social-v1/examples/content-calendar-sample.md +74 -0
  16. package/assets/worker-kits/growthub-zernio-social-v1/examples/social-campaign-sample.md +105 -0
  17. package/assets/worker-kits/growthub-zernio-social-v1/growthub-meta/README.md +146 -0
  18. package/assets/worker-kits/growthub-zernio-social-v1/growthub-meta/kit-standard.md +120 -0
  19. package/assets/worker-kits/growthub-zernio-social-v1/kit.json +104 -0
  20. package/assets/worker-kits/growthub-zernio-social-v1/output/README.md +63 -0
  21. package/assets/worker-kits/growthub-zernio-social-v1/output-standards.md +132 -0
  22. package/assets/worker-kits/growthub-zernio-social-v1/runtime-assumptions.md +170 -0
  23. package/assets/worker-kits/growthub-zernio-social-v1/setup/check-deps.mjs +117 -0
  24. package/assets/worker-kits/growthub-zernio-social-v1/setup/check-deps.sh +86 -0
  25. package/assets/worker-kits/growthub-zernio-social-v1/setup/install-mcp.mjs +177 -0
  26. package/assets/worker-kits/growthub-zernio-social-v1/setup/setup.mjs +247 -0
  27. package/assets/worker-kits/growthub-zernio-social-v1/setup/verify-env.mjs +138 -0
  28. package/assets/worker-kits/growthub-zernio-social-v1/skills.md +332 -0
  29. package/assets/worker-kits/growthub-zernio-social-v1/templates/analytics-brief.md +101 -0
  30. package/assets/worker-kits/growthub-zernio-social-v1/templates/caption-copy-deck.md +105 -0
  31. package/assets/worker-kits/growthub-zernio-social-v1/templates/client-proposal.md +98 -0
  32. package/assets/worker-kits/growthub-zernio-social-v1/templates/content-calendar.md +70 -0
  33. package/assets/worker-kits/growthub-zernio-social-v1/templates/platform-publishing-plan.md +86 -0
  34. package/assets/worker-kits/growthub-zernio-social-v1/templates/scheduling-manifest.md +92 -0
  35. package/assets/worker-kits/growthub-zernio-social-v1/templates/social-campaign-brief.md +102 -0
  36. package/assets/worker-kits/growthub-zernio-social-v1/validation-checklist.md +85 -0
  37. package/assets/worker-kits/growthub-zernio-social-v1/workers/zernio-social-operator/CLAUDE.md +307 -0
  38. package/package.json +1 -1
@@ -0,0 +1,166 @@
1
+ # Postiz UI Shell + Zernio Engine — Integration Recipe
2
+
3
+ This document is the canonical recipe for running `growthub-zernio-social-v1` as the engine layer underneath the stable `growthub-postiz-social-v1` kit's UI shell.
4
+
5
+ **Read order:** `../runtime-assumptions.md` → `./zernio-api-integration.md` → this file.
6
+
7
+ ---
8
+
9
+ ## Architectural split
10
+
11
+ | Layer | Owned by | What it does |
12
+ |---|---|---|
13
+ | Presentation | `growthub-postiz-social-v1` (Postiz UI) | Calendar, compose, analytics shell, team workspace |
14
+ | Engine | `growthub-zernio-social-v1` (this kit) | Posts, queues, media, inbox, analytics transport against Zernio REST API |
15
+
16
+ **Core principle:** Postiz stays the UI. Zernio replaces Postiz's native provider/posting engine. Nothing in Postiz's own database schema, Redis queue runner, React app, or auth/team/workspace system changes. The diff surface is the provider + publish-bridge layers only.
17
+
18
+ ---
19
+
20
+ ## The 7 Module Integration Map
21
+
22
+ ### Module 1 — Provider Override (Zernio as the transport)
23
+
24
+ Postiz's provider system is where per-platform OAuth and posting normally live. Replace it with a single `ZernioProvider`:
25
+
26
+ - `baseUrl` → `https://zernio.com/api/v1`
27
+ - `authHeader` → `Authorization: Bearer ${ZERNIO_API_KEY}`
28
+ - All 14 platforms route through this one provider — no per-platform OAuth apps on the Postiz side
29
+ - Postiz's "Connect Account" UI validates `ZERNIO_API_KEY` instead of OAuth dancing — Zernio handles all OAuth upstream
30
+
31
+ The config for this provider mirrors exactly `buildZernioSocialConfig()` in `cli/src/kits/core/index.ts`:
32
+
33
+ ```ts
34
+ {
35
+ providerId: "zernio",
36
+ providerName: "Zernio (hosted)",
37
+ providerBaseUrl: "https://zernio.com/api/v1",
38
+ providerAuthField: "Authorization",
39
+ apiKeyEnvVar: "ZERNIO_API_KEY",
40
+ additionalRequiredEnvVars: ["ZERNIO_API_URL"],
41
+ }
42
+ ```
43
+
44
+ ### Module 2 — Post Submission Bridge
45
+
46
+ The Zernio operator already produces manifests shaped as valid `POST /api/v1/posts` bodies. The bridge:
47
+
48
+ 1. Intercepts Postiz's internal `publishPost()` call
49
+ 2. Transforms the Postiz post into a Zernio manifest entry (see `./posts-and-queues-layer.md`)
50
+ 3. Fires `POST ${ZERNIO_API_URL}/posts` with `Authorization: Bearer ${ZERNIO_API_KEY}` and `Idempotency-Key: <clientPostId>`
51
+ 4. Feeds Zernio's response back into Postiz's job tracker
52
+
53
+ `clientPostId` format stays `<client-slug>-<YYYYMMDD>-<sequence>`. Re-submitting the same manifest under the same key is safe.
54
+
55
+ ### Module 3 — Queue Sync Layer
56
+
57
+ Postiz's queue concept maps 1:1 onto Zernio queues (`POST /api/v1/queues`):
58
+
59
+ - Postiz queue scheduler → Zernio `queues` endpoint (create) / `queues/<queueId>` (update)
60
+ - `Idempotency-Key: queue-<name>` prevents double-fire on retry
61
+ - Posts attached to a queue omit `scheduledFor` and include `queueId`
62
+ - The 10-step `zernio-social-operator` workflow becomes the execution contract for each queue job
63
+
64
+ ### Module 4 — AI Caption Layer Surface
65
+
66
+ The agent-side `/zernio` command surface replaces Postiz's native AI composer:
67
+
68
+ - Postiz compose textarea → `/zernio captions` (or `/zernio campaign` for full scope)
69
+ - Caption drafts come from the A/B/C variant rules in `./ai-caption-layer.md`
70
+ - Claude Code runs as a background operator session; Postiz compose UI is the front-end trigger
71
+ - The 10 `/zernio` subcommands wire into Postiz's compose surface as slash-quick-actions
72
+
73
+ ### Module 5 — Platform Coverage Config
74
+
75
+ `./platform-coverage.md` is the source of truth for Postiz channel configuration:
76
+
77
+ - Disable native Postiz channel handlers
78
+ - Register 14 channels pointing to the Zernio transport from Module 1
79
+ - The per-platform format spec (char limits, aspect ratios, post types, carousel eligibility, thread support, cadence) drives Postiz's per-channel validation + preview rendering
80
+ - `skills.md` per-platform tone rules feed the compose UI's voice hints
81
+
82
+ ### Module 6 — ENV + Secret Surface
83
+
84
+ Direct alignment — the cleanest module:
85
+
86
+ | Kit env var | Postiz field |
87
+ |---|---|
88
+ | `ZERNIO_API_KEY` | provider credentials field (replaces all per-platform OAuth tokens) |
89
+ | `ZERNIO_API_URL` | provider baseUrl override (regional/proxy deployments only) |
90
+ | `ZERNIO_PROFILE_ID` | default profile scope for all write requests |
91
+ | `ZERNIO_TIMEZONE` | default posting timezone when the profile's timezone isn't used |
92
+
93
+ `setup/verify-env.mjs` and `setup/check-deps.sh` run as Postiz's pre-start health checks. The secret-hygiene scan (`sk_` + 64-hex leak detection from the kit test suite) is the canonical gate on any output or log line.
94
+
95
+ ### Module 7 — Workspace CLI Entry Point
96
+
97
+ `@growthub/cli >= 0.3.57` is the operator terminal that sits beside Postiz:
98
+
99
+ - `growthub kit download growthub-zernio-social-v1` — materialize the kit
100
+ - `growthub kit path growthub-zernio-social-v1` — print the working-directory path
101
+ - Point a Claude Code session at that folder; the operator handles the full 10-step workflow
102
+ - Postiz UI reflects the output — scheduled posts, queue runs, generated captions, analytics
103
+ - The 7 artifacts declared by `buildZernioSocialConfig()` (brief, calendar, publishing plan, caption deck, scheduling manifest, analytics brief, client proposal) are the canonical source of truth for what the UI shows
104
+
105
+ ---
106
+
107
+ ## Integration Sequence (request path)
108
+
109
+ ```
110
+ Postiz UI (compose / schedule)
111
+
112
+ Module 2 — Post Submission Bridge
113
+
114
+ Module 1 — ZernioProvider (Authorization: Bearer ZERNIO_API_KEY)
115
+
116
+ Zernio API → POST /api/v1/posts
117
+ + POST /api/v1/queues
118
+ + POST /api/v1/media
119
+ + GET /api/v1/inbox
120
+ + GET /api/v1/analytics/*
121
+
122
+ Module 3 — Queue Sync reads back status
123
+
124
+ Postiz Calendar / Analytics shell renders state
125
+ ```
126
+
127
+ Claude Code + the `/zernio` surface sits **laterally** — it feeds the compose box in Postiz before submission, not inside the request path. This keeps the agent lane non-blocking for end-user UI latency.
128
+
129
+ ---
130
+
131
+ ## What Stays Untouched in Postiz
132
+
133
+ | Layer | Status |
134
+ |---|---|
135
+ | Postgres schema | Unchanged |
136
+ | Redis queue runner | Unchanged (different job payloads, same runner) |
137
+ | React frontend | Unchanged (except the compose AI hook in Module 4) |
138
+ | Auth / team / workspace | Unchanged |
139
+ | Existing Postiz kit payload (`growthub-postiz-social-v1`) | Unchanged — it remains valid for self-hosted-only operators |
140
+
141
+ This keeps the diff surface minimal and keeps `growthub-zernio-social-v1` fully isolated as the engine layer.
142
+
143
+ ---
144
+
145
+ ## Operator Setup Order (when paired with Postiz UI shell)
146
+
147
+ 1. Stand up Postiz via its own kit (`growthub kit download growthub-postiz-social-v1`) — optional if only the UI layer is desired
148
+ 2. Download this kit: `growthub kit download growthub-zernio-social-v1`
149
+ 3. Fill `.env` — `ZERNIO_API_KEY`, `ZERNIO_API_URL`, `ZERNIO_PROFILE_ID`
150
+ 4. Run `node setup/verify-env.mjs` (kit-level) and the Postiz pre-start checks
151
+ 5. Apply Modules 1–3 inside the Postiz fork (provider override, publish bridge, queue sync). Everything else stays default.
152
+ 6. Open Claude Code at the Zernio kit's exported folder for the `/zernio` surface
153
+
154
+ Standalone (without Postiz UI): everything still works — the kit is self-contained, and Modules 1–3 are optional.
155
+
156
+ ---
157
+
158
+ ## Validation Before Go-Live
159
+
160
+ - [ ] Zernio API key scope is `read-write`
161
+ - [ ] `GET /api/v1/accounts?profileId=...` returns at least one connected platform
162
+ - [ ] A dry-run manifest round-trips through Module 2 with a successful 2xx from `POST /api/v1/posts`
163
+ - [ ] A queue definition round-trips through Module 3 with a returned `queueId`
164
+ - [ ] Postiz channel list surfaces all 14 Zernio platform slugs from `./platform-coverage.md`
165
+ - [ ] Compose surface calls into `/zernio captions` and renders A/B/C variants
166
+ - [ ] Kit secret-hygiene scan on the Postiz fork's logs is clean
@@ -0,0 +1,208 @@
1
+ # Posts + Queues Layer
2
+
3
+ This document specifies the JSON shapes the operator must produce. Every shape is a valid request body for the corresponding Zernio REST endpoint, so manifests can be piped directly into `curl` or the Zernio SDKs with no re-shaping.
4
+
5
+ ---
6
+
7
+ ## Scheduling Manifest
8
+
9
+ The scheduling manifest is the machine-readable record the operator writes to `output/<client-slug>/<project-slug>/scheduling-manifest.json` whenever scheduling is requested.
10
+
11
+ ### Top-level shape
12
+
13
+ ```json
14
+ {
15
+ "zernioSchedulingManifest": {
16
+ "version": "1.0",
17
+ "profileId": "<ZERNIO_PROFILE_ID>",
18
+ "timezone": "America/New_York",
19
+ "dryRun": false,
20
+ "generatedAt": "2026-04-15T14:30:00-04:00",
21
+ "notes": "<free-form operator notes — kept with the manifest>",
22
+ "posts": [ /* post entries */ ]
23
+ }
24
+ }
25
+ ```
26
+
27
+ | Field | Required | Type | Notes |
28
+ |---|---|---|---|
29
+ | `version` | Yes | string | Manifest schema version. Current: `"1.0"`. |
30
+ | `profileId` | Yes | string | Must be a real Zernio profile id in api-live mode. In agent-only mode may be `"placeholder"` provided `dryRun: true`. |
31
+ | `timezone` | Yes | string | IANA name. Default: value of `ZERNIO_TIMEZONE` or the profile's default. |
32
+ | `dryRun` | Yes | boolean | `true` in agent-only mode. `false` in api-live and hybrid modes. |
33
+ | `generatedAt` | Yes | string | ISO 8601 with tz offset. |
34
+ | `notes` | No | string | Free-form notes — kept with the manifest for audit. |
35
+ | `posts` | Yes | array | 1..N entries. |
36
+
37
+ ### Per-post entry shape
38
+
39
+ Each entry is a direct body for `POST /api/v1/posts`, with two additional fields the operator uses for bookkeeping (`clientPostId`, `status`).
40
+
41
+ ```json
42
+ {
43
+ "clientPostId": "urban-cycle-20260501-001",
44
+ "content": "Launch day — shipping our spring gravel lineup. Ride it today →",
45
+ "scheduledFor": "2026-05-01T09:00:00-04:00",
46
+ "timezone": "America/New_York",
47
+ "media": [{ "mediaId": "med_HERO_01" }],
48
+ "platforms": [
49
+ { "platform": "instagram", "accountId": "acc_ig_UrbanCycle" },
50
+ { "platform": "twitter", "accountId": "acc_x_UrbanCycle" }
51
+ ],
52
+ "status": "pending"
53
+ }
54
+ ```
55
+
56
+ | Field | Required | Type | Notes |
57
+ |---|---|---|---|
58
+ | `clientPostId` | Yes | string | Operator-generated id. Format: `<client-slug>-<YYYYMMDD>-<sequence>`. Used as the `Idempotency-Key` when submitting to Zernio. Not sent in the body. |
59
+ | `content` | Yes | string | The selected caption variant. |
60
+ | `scheduledFor` | Conditional | string | ISO 8601 with tz offset. Required unless the post is attached to a queue via `queueId`. |
61
+ | `timezone` | Yes | string | IANA name. |
62
+ | `media` | No | array | Array of `{ "mediaId": "<id>" }`. In api-live mode must reference real Zernio `mediaId` values obtained from `POST /api/v1/media`. |
63
+ | `platforms` | Yes | array | 1..N `{ platform, accountId }`. Every `platform` must exist in `docs/platform-coverage.md`. |
64
+ | `queueId` | Conditional | string | Only when the post is attached to a queue. Mutually exclusive with `scheduledFor`. |
65
+ | `status` | Yes | string | Operator bookkeeping. Always starts at `"pending"` and transitions to `"scheduled"` / `"published"` / `"failed"` once Zernio confirms. |
66
+
67
+ ### Submitting a manifest
68
+
69
+ Each entry becomes one Zernio POST:
70
+
71
+ ```bash
72
+ curl -sS -X POST \
73
+ "${ZERNIO_API_URL:-https://zernio.com/api/v1}/posts" \
74
+ -H "Authorization: Bearer ${ZERNIO_API_KEY}" \
75
+ -H "Content-Type: application/json" \
76
+ -H "Idempotency-Key: <clientPostId>" \
77
+ --data-binary @- <<'JSON'
78
+ {
79
+ "profileId": "prof_abc123",
80
+ "content": "...",
81
+ "scheduledFor": "2026-05-01T09:00:00-04:00",
82
+ "timezone": "America/New_York",
83
+ "media": [{ "mediaId": "med_HERO_01" }],
84
+ "platforms": [{ "platform": "twitter", "accountId": "acc_x_111" }]
85
+ }
86
+ JSON
87
+ ```
88
+
89
+ The operator is responsible for issuing one request per `posts[]` entry and for retrying on 429/5xx with the same `Idempotency-Key`.
90
+
91
+ ---
92
+
93
+ ## Recurring Queue Definition
94
+
95
+ Queues define a repeating schedule on a profile. Posts attached to a queue auto-schedule into the next available slot.
96
+
97
+ ### Queue shape
98
+
99
+ Body of `POST /api/v1/queues` and `PUT /api/v1/queues/<queueId>`:
100
+
101
+ ```json
102
+ {
103
+ "profileId": "prof_abc123",
104
+ "name": "weekly-evergreen",
105
+ "timezone": "America/New_York",
106
+ "slots": [
107
+ { "day": "mon", "time": "09:00", "platforms": ["twitter", "linkedin"] },
108
+ { "day": "wed", "time": "12:30", "platforms": ["instagram"] },
109
+ { "day": "fri", "time": "17:00", "platforms": ["bluesky", "threads"] }
110
+ ]
111
+ }
112
+ ```
113
+
114
+ | Field | Required | Type | Notes |
115
+ |---|---|---|---|
116
+ | `profileId` | Yes | string | Zernio profile owning the queue. |
117
+ | `name` | Yes | string | Lowercase kebab-case recommended. |
118
+ | `timezone` | Yes | string | IANA name. |
119
+ | `slots` | Yes | array | 1..N slots. |
120
+ | `slots[].day` | Yes | string | One of `mon|tue|wed|thu|fri|sat|sun`. |
121
+ | `slots[].time` | Yes | string | `HH:MM` 24-hour. |
122
+ | `slots[].platforms` | Yes | array | Platforms enabled for this slot. |
123
+
124
+ ### Post attached to a queue
125
+
126
+ ```json
127
+ {
128
+ "profileId": "prof_abc123",
129
+ "queueId": "que_xyz789",
130
+ "content": "Weekend builder log: three wins, one lesson.",
131
+ "platforms": [{ "platform": "twitter", "accountId": "acc_x_111" }]
132
+ }
133
+ ```
134
+
135
+ The request is still `POST /api/v1/posts` but with `queueId` instead of `scheduledFor`. Zernio returns the computed `scheduledFor` in the response.
136
+
137
+ ### Queue deliverable in the kit
138
+
139
+ The operator writes a queue definition alongside the scheduling manifest as:
140
+
141
+ ```
142
+ output/<client-slug>/<project-slug>/queue-<queue-name>.json
143
+ ```
144
+
145
+ The file contains one JSON object — the `zernioQueue` wrapper below — so that it reads as documentation plus a ready-to-submit request body.
146
+
147
+ ```json
148
+ {
149
+ "zernioQueue": {
150
+ "version": "1.0",
151
+ "profileId": "<ZERNIO_PROFILE_ID>",
152
+ "dryRun": false,
153
+ "queue": {
154
+ "profileId": "<ZERNIO_PROFILE_ID>",
155
+ "name": "weekly-evergreen",
156
+ "timezone": "America/New_York",
157
+ "slots": [
158
+ { "day": "mon", "time": "09:00", "platforms": ["twitter", "linkedin"] }
159
+ ]
160
+ }
161
+ }
162
+ }
163
+ ```
164
+
165
+ ---
166
+
167
+ ## Media Upload
168
+
169
+ Every image or video referenced by a post must be uploaded first.
170
+
171
+ ```bash
172
+ curl -sS -X POST \
173
+ "${ZERNIO_API_URL:-https://zernio.com/api/v1}/media" \
174
+ -H "Authorization: Bearer ${ZERNIO_API_KEY}" \
175
+ -H "Idempotency-Key: media-urban-cycle-20260501-hero-01" \
176
+ -F "file=@./media/urban-cycle/hero-01.png" \
177
+ -F "profileId=prof_abc123"
178
+ ```
179
+
180
+ The response includes `mediaId`. That id is what the scheduling manifest references.
181
+
182
+ In `agent-only` mode the operator emits placeholder `mediaId` values prefixed with `placeholder_` and documents the required asset in the Caption Copy Deck's "Media Notes" column. The user uploads the real media and replaces placeholders before submitting.
183
+
184
+ ---
185
+
186
+ ## Idempotency Contract
187
+
188
+ | Endpoint | Idempotency key source |
189
+ |---|---|
190
+ | `POST /api/v1/posts` | `clientPostId` from the manifest entry |
191
+ | `POST /api/v1/queues` | `queue.name` prefixed with `queue-` |
192
+ | `POST /api/v1/media` | `media-<client-slug>-<YYYYMMDD>-<asset-slug>` |
193
+
194
+ The operator must NEVER send different bodies under the same `Idempotency-Key`. If content changes, increment the manifest version and use a new key.
195
+
196
+ ---
197
+
198
+ ## Validation Rules (pre-submission)
199
+
200
+ Before any submission, re-run through `validation-checklist.md`. Specifically:
201
+
202
+ - Every `posts[].platforms[].platform` exists in `docs/platform-coverage.md`
203
+ - Every `clientPostId` matches the filename conventions in `output-standards.md`
204
+ - `scheduledFor` timestamps all fall inside the campaign window from the Social Campaign Brief
205
+ - `dryRun` matches the declared execution mode
206
+ - No post entry exceeds per-platform character limits from `docs/ai-caption-layer.md`
207
+
208
+ Failure on any of these is a blocker — fix the manifest before submitting.
@@ -0,0 +1,265 @@
1
+ # Zernio API Integration
2
+
3
+ This document is the reference contract between the `growthub-zernio-social-v1` kit and the [Zernio](https://zernio.com) REST API. It is frozen at kit creation and should be updated when the upstream API contract changes.
4
+
5
+ > Source of truth upstream: [docs.zernio.com](https://docs.zernio.com). When this document conflicts with live Zernio docs, the live docs win; update this file and the kit version.
6
+
7
+ ---
8
+
9
+ ## Base Contract
10
+
11
+ | Field | Value |
12
+ |---|---|
13
+ | Base URL | `https://zernio.com/api/v1` |
14
+ | Env var | `ZERNIO_API_URL` (override only for regional / proxy deployments) |
15
+ | Transport | HTTPS only |
16
+ | Request content type | `application/json` (JSON endpoints) · `multipart/form-data` (`/media` upload) |
17
+ | Response content type | `application/json` |
18
+ | Character encoding | UTF-8 |
19
+
20
+ ---
21
+
22
+ ## Authentication
23
+
24
+ Every request carries a single bearer header:
25
+
26
+ ```
27
+ Authorization: Bearer ${ZERNIO_API_KEY}
28
+ ```
29
+
30
+ ### Key format
31
+
32
+ - Prefix: `sk_`
33
+ - Body: 64 hex characters
34
+ - Total length: 67 characters
35
+
36
+ Regex: `^sk_[0-9a-fA-F]{64}$`
37
+
38
+ ### Key scopes
39
+
40
+ | Scope | Purpose |
41
+ |---|---|
42
+ | `read` | Read-only endpoints only (profiles, accounts, analytics, inbox listing) |
43
+ | `read-write` | Everything in `read` plus create/update posts, queues, media, and inbox replies |
44
+
45
+ ### Key scope filter
46
+
47
+ An individual key may be scoped `full` (any profile on the account) or `profiles-specific` (restricted to a list of profile IDs).
48
+
49
+ ### Idempotency
50
+
51
+ Attach an `Idempotency-Key` header to every write request. The operator uses the `clientPostId` from the scheduling manifest as the idempotency key so the manifest can be re-submitted safely.
52
+
53
+ ---
54
+
55
+ ## Core Resource Model
56
+
57
+ Zernio models social publishing around five primary resources:
58
+
59
+ | Resource | Endpoint root | Role |
60
+ |---|---|---|
61
+ | Profiles | `/api/v1/profiles` | A container that groups social accounts. Every request is implicitly scoped to one profile for scheduling. |
62
+ | Accounts | `/api/v1/accounts` | A connected social account that belongs to a profile. Identified by `accountId`. |
63
+ | Posts | `/api/v1/posts` | Schedulable content. One post can fan out to many `{ platform, accountId }` pairs. |
64
+ | Queues | `/api/v1/queues` | Recurring time-slot schedule attached to a profile. Posts added to a queue auto-schedule into the next open slot. |
65
+ | Media | `/api/v1/media` | Image, video, and document assets uploaded once and referenced by `mediaId` from a post body. |
66
+
67
+ Two secondary resources:
68
+
69
+ | Resource | Endpoint root | Role |
70
+ |---|---|---|
71
+ | Inbox | `/api/v1/inbox` | Unified DM, comment, and review conversations aggregated per profile. |
72
+ | Analytics | `/api/v1/analytics` | Per-post and per-account metrics. |
73
+
74
+ Supporting endpoints:
75
+
76
+ | Endpoint | Role |
77
+ |---|---|
78
+ | `/api/v1/api-keys` | Create, list, rotate, and revoke API keys |
79
+ | `/api/v1/connect/<platform>` | Begin platform OAuth / credential flow for a new account on a profile |
80
+ | `/api/v1/platforms` | Live list of supported platforms and their per-platform capability flags |
81
+
82
+ Extended capability surface (confirmed in `zernio-cli`, may be plan-gated):
83
+
84
+ | Resource | Endpoint root | Role |
85
+ |---|---|---|
86
+ | Contacts | `/api/v1/contacts` | CRUD + bulk create + custom fields |
87
+ | Broadcasts | `/api/v1/broadcasts` | Create / schedule / send / cancel + recipient management |
88
+ | Sequences | `/api/v1/sequences` | Create / activate / pause + enrollment management |
89
+ | Automations | `/api/v1/automations` | Create / run / inspect execution logs |
90
+ | Webhooks | `/api/v1/webhooks` | Settings (create/update) + logs + signature verification; events include `account.connected`, `post.recycled` |
91
+
92
+ The zernio-social-operator uses these endpoints only when the user explicitly asks for CRM-shaped work (contacts / broadcasts / sequences) or wants an automation wired into a campaign. Default campaign flow stays scoped to posts + queues + media + inbox + analytics.
93
+
94
+ ---
95
+
96
+ ## Endpoints Used By This Kit
97
+
98
+ ### Profiles
99
+
100
+ - `GET /api/v1/profiles` — list profiles on the account
101
+ - `GET /api/v1/profiles/<profileId>` — fetch a single profile, including the default timezone
102
+
103
+ ### Accounts
104
+
105
+ - `GET /api/v1/accounts?profileId=<id>` — list connected accounts on a profile (platform + handle + accountId)
106
+
107
+ ### Media upload
108
+
109
+ - `POST /api/v1/media` — multipart upload; response includes `mediaId`
110
+ - `GET /api/v1/media/<mediaId>` — fetch metadata of an uploaded asset
111
+
112
+ ### Posts
113
+
114
+ - `POST /api/v1/posts` — schedule one post with fan-out targets
115
+ - `GET /api/v1/posts?profileId=<id>&status=scheduled` — list scheduled posts
116
+ - `GET /api/v1/posts/<postId>` — fetch a single post
117
+ - `DELETE /api/v1/posts/<postId>` — unschedule a post (only while `status=scheduled`)
118
+
119
+ Minimal `POST /api/v1/posts` body:
120
+
121
+ ```json
122
+ {
123
+ "profileId": "prof_abc123",
124
+ "content": "Launch day — shipping our new kit. →",
125
+ "scheduledFor": "2026-05-01T09:00:00-04:00",
126
+ "timezone": "America/New_York",
127
+ "media": [{ "mediaId": "med_imgA" }],
128
+ "platforms": [
129
+ { "platform": "twitter", "accountId": "acc_x_111" },
130
+ { "platform": "linkedin", "accountId": "acc_li_222" }
131
+ ]
132
+ }
133
+ ```
134
+
135
+ ### Queues (recurring schedules)
136
+
137
+ - `POST /api/v1/queues` — create a recurring queue
138
+ - `GET /api/v1/queues?profileId=<id>` — list queues on a profile
139
+ - `PUT /api/v1/queues/<queueId>` — replace queue configuration
140
+ - `DELETE /api/v1/queues/<queueId>` — remove queue (future slots stop; already-scheduled posts remain)
141
+
142
+ Minimal queue body:
143
+
144
+ ```json
145
+ {
146
+ "profileId": "prof_abc123",
147
+ "name": "weekly-evergreen",
148
+ "timezone": "America/New_York",
149
+ "slots": [
150
+ { "day": "mon", "time": "09:00", "platforms": ["twitter", "linkedin"] },
151
+ { "day": "wed", "time": "12:30", "platforms": ["instagram"] },
152
+ { "day": "fri", "time": "17:00", "platforms": ["bluesky", "threads"] }
153
+ ]
154
+ }
155
+ ```
156
+
157
+ Posts attached to a queue omit `scheduledFor` and include `queueId` instead:
158
+
159
+ ```json
160
+ {
161
+ "profileId": "prof_abc123",
162
+ "queueId": "que_xyz789",
163
+ "content": "Weekend builder log: three wins, one lesson.",
164
+ "platforms": [{ "platform": "twitter", "accountId": "acc_x_111" }]
165
+ }
166
+ ```
167
+
168
+ ### Inbox (DMs, comments, reviews)
169
+
170
+ - `GET /api/v1/inbox?profileId=<id>` — unified conversation list
171
+ - `GET /api/v1/inbox/<conversationId>` — conversation thread
172
+ - `POST /api/v1/inbox/<conversationId>/reply` — reply to a conversation
173
+
174
+ ### Analytics
175
+
176
+ - `GET /api/v1/analytics/posts?profileId=<id>&from=<date>&to=<date>` — per-post metrics
177
+ - `GET /api/v1/analytics/accounts?profileId=<id>&from=<date>&to=<date>` — per-account summary
178
+
179
+ ### API keys
180
+
181
+ - `POST /api/v1/api-keys` — create a new key with `scope` and `permission`
182
+ - `GET /api/v1/api-keys` — list existing keys
183
+ - `DELETE /api/v1/api-keys/<keyId>` — revoke a key
184
+
185
+ ---
186
+
187
+ ## Error Model
188
+
189
+ All non-2xx responses return:
190
+
191
+ ```json
192
+ {
193
+ "error": {
194
+ "code": "<machine_readable_code>",
195
+ "message": "<human readable>",
196
+ "requestId": "<uuid>"
197
+ }
198
+ }
199
+ ```
200
+
201
+ Well-known codes the operator handles:
202
+
203
+ | HTTP | Zernio code | Behavior |
204
+ |---|---|---|
205
+ | 401 | `auth_invalid` | Key missing / malformed / revoked — surface to user, fall back to agent-only |
206
+ | 403 | `permission_denied` | Key lacks `read-write` scope — surface with rotate-instructions |
207
+ | 404 | `profile_not_found` / `account_not_found` | Correct the id or fall back to dry-run |
208
+ | 409 | `conflict` | Typically an idempotency collision; safe to treat as success |
209
+ | 422 | `validation_failed` | Manifest shape bug — fix and retry |
210
+ | 429 | `rate_limited` | Back off using `Retry-After` header |
211
+ | 5xx | `internal_error` | Retry with exponential backoff up to 3 attempts |
212
+
213
+ ---
214
+
215
+ ## Plans and Quotas (as-of kit freeze)
216
+
217
+ | Plan | Monthly | Annual (per mo) | Profiles | Posts / month | Notes |
218
+ |---|---|---|---|---|---|
219
+ | Free | $0 | $0 | 2 | 20 | Full REST API access; useful for prototypes |
220
+ | Build | $19 | $16 | 10 | 120 | First paid tier |
221
+ | Accelerate | $49 | $41 | 50 | unlimited | Recommended for agencies + multi-client operators |
222
+ | Unlimited | $999 | $833 | unlimited | unlimited | Enterprise; includes higher support SLA |
223
+
224
+ All plans ship the same REST contract (posts, queues, accounts, profiles, media, inbox, analytics, contacts, broadcasts, sequences, automations, webhooks, api-keys, connect, platforms). The post-count gate is the main throttle across tiers. Rate limit is 60 requests/minute per API key at every tier.
225
+
226
+ The operator must degrade gracefully when a quota is hit: surface the Zernio error code (`rate_limited`, `quota_exhausted`) to the user, offer to switch the session into `agent-only` mode, and never silently drop scheduled posts.
227
+
228
+ ## Rate Limit Handling
229
+
230
+ Default plan: 60 requests/minute per API key. The operator batches reads during account inspection and never issues more than one write per post per second.
231
+
232
+ When a 429 is returned:
233
+
234
+ 1. Read `Retry-After` header (seconds)
235
+ 2. Sleep for the returned value plus 500ms jitter
236
+ 3. Retry the same request with the same `Idempotency-Key`
237
+ 4. On third failure, stop and report to the user
238
+
239
+ ---
240
+
241
+ ## SDK + Harness Options (informational)
242
+
243
+ Zernio ships official SDKs for Node.js, Python, Go, Ruby, Java, PHP, .NET, and Rust. Source repos live under [github.com/zernio-dev](https://github.com/zernio-dev).
244
+
245
+ Two primitives are directly relevant to local AI coding environments:
246
+
247
+ | Primitive | Distribution | Use from | Install |
248
+ |---|---|---|---|
249
+ | Official MCP server | bundled inside `zernio-python` | Claude Desktop, Claude Code, Cursor, any MCP-compatible IDE | `pip install zernio-sdk[mcp]` |
250
+ | Zernio API Claude Code skill | `zernio-api` repo | Claude Code only | `npx clawhub@latest install zernio-api` |
251
+
252
+ This kit intentionally uses the **raw REST contract** via Node's built-in `fetch()` so the operator behaves identically regardless of which local IDE the user has installed. If the user additionally plugs in Zernio's MCP server or the Claude Code skill, that is strictly complementary — the kit does not depend on either.
253
+
254
+ See `docs/local-adapters.md` for the per-IDE setup matrix and `setup/install-mcp.mjs` for the copy-paste MCP config JSON blocks.
255
+
256
+ Kit files never install or require any Zernio SDK. If the user wants to install one locally for their own tooling, that is their choice and does not affect kit behavior.
257
+
258
+ ---
259
+
260
+ ## Security
261
+
262
+ - `ZERNIO_API_KEY` lives only in `.env` (ignored by git) and the runtime environment
263
+ - Outputs never include the raw key, request headers, or response payloads that contain keys
264
+ - `scheduling-manifest.json` contains only `clientPostId`, content, timestamps, and Zernio resource IDs — no secrets
265
+ - Any snippet of curl help in documentation uses `${ZERNIO_API_KEY}` as an expanded env var reference, never a literal