From c09dc3b9c17612c9d4940b0aad1f2982c99cbfbc Mon Sep 17 00:00:00 2001 From: Recoup CTO Agent Date: Tue, 24 Mar 2026 16:23:13 +0000 Subject: [PATCH 1/4] docs: add Recoup Content Agent guide and API reference - Guide page covering @mention syntax, data flow, setup, and troubleshooting - OpenAPI spec entries for POST /api/content-agent/{platform} and POST /api/content-agent/callback - API reference pages for webhook and callback endpoints - Navigation updated with Agents group in Guides tab and Content Agent group in API reference Co-Authored-By: Paperclip --- api-reference/content-agent/callback.mdx | 4 + api-reference/content-agent/webhook.mdx | 4 + api-reference/openapi.json | 140 +++++++++++++++++++++++ content-agent.mdx | 128 +++++++++++++++++++++ docs.json | 13 +++ 5 files changed, 289 insertions(+) create mode 100644 api-reference/content-agent/callback.mdx create mode 100644 api-reference/content-agent/webhook.mdx create mode 100644 content-agent.mdx diff --git a/api-reference/content-agent/callback.mdx b/api-reference/content-agent/callback.mdx new file mode 100644 index 0000000..1be4bdf --- /dev/null +++ b/api-reference/content-agent/callback.mdx @@ -0,0 +1,4 @@ +--- +title: 'Task Callback' +openapi: 'POST /api/content-agent/callback' +--- diff --git a/api-reference/content-agent/webhook.mdx b/api-reference/content-agent/webhook.mdx new file mode 100644 index 0000000..723d8bc --- /dev/null +++ b/api-reference/content-agent/webhook.mdx @@ -0,0 +1,4 @@ +--- +title: 'Slack Webhook' +openapi: 'POST /api/content-agent/{platform}' +--- diff --git a/api-reference/openapi.json b/api-reference/openapi.json index ce3654a..e1b1489 100644 --- a/api-reference/openapi.json +++ b/api-reference/openapi.json @@ -4500,6 +4500,146 @@ } } } + }, + "/api/content-agent/{platform}": { + "post": { + "description": "Webhook endpoint for the Recoup Content Agent Slack bot. Receives @mention events from Slack and triggers content generation for the mentioned artist. The bot parses the mention text for ` [template] [batch=N] [lipsync]`, validates the artist, calls POST /api/content/create, and starts a background polling task that reports results back to the Slack thread.\n\nFor Slack, also handles `url_verification` challenges during app setup.", + "parameters": [ + { + "name": "platform", + "in": "path", + "description": "Chat platform identifier. Currently supports `slack`.", + "required": true, + "schema": { + "type": "string", + "enum": ["slack"] + } + } + ], + "requestBody": { + "description": "Slack Events API payload (app_mention event or url_verification challenge)", + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "Slack Events API envelope — the shape depends on the event type" + } + } + } + }, + "responses": { + "200": { + "description": "Event processed successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "ok": { + "type": "boolean" + } + } + } + } + } + }, + "404": { + "description": "Unknown platform" + } + } + } + }, + "/api/content-agent/callback": { + "post": { + "description": "Internal callback endpoint for the `poll-content-run` Trigger.dev task. Receives content generation results and posts them back to the originating Slack thread. Authenticated via the `x-callback-secret` header.\n\nThis endpoint is not intended for external use — it is called automatically by the polling task when content runs complete, fail, or time out.", + "requestBody": { + "description": "Content generation results from the polling task", + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["threadId", "status"], + "properties": { + "threadId": { + "type": "string", + "description": "Chat SDK thread identifier for the originating Slack thread" + }, + "status": { + "type": "string", + "enum": ["completed", "failed", "timeout"], + "description": "Overall status of the content generation batch" + }, + "results": { + "type": "array", + "description": "Per-run results", + "items": { + "type": "object", + "required": ["runId", "status"], + "properties": { + "runId": { + "type": "string", + "description": "Trigger.dev run ID" + }, + "status": { + "type": "string", + "enum": ["completed", "failed", "timeout"] + }, + "videoUrl": { + "type": "string", + "description": "URL of the generated video (when completed)" + }, + "captionText": { + "type": "string", + "description": "Generated caption text (when completed)" + }, + "error": { + "type": "string", + "description": "Error message (when failed)" + } + } + } + }, + "message": { + "type": "string", + "description": "Optional human-readable message" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Callback processed and results posted to Slack", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "status": { + "type": "string", + "example": "ok" + } + } + } + } + } + }, + "401": { + "description": "Missing or invalid callback secret" + }, + "400": { + "description": "Invalid request body" + } + }, + "security": [ + { + "callbackSecret": [] + } + ] + } } }, "components": { diff --git a/content-agent.mdx b/content-agent.mdx new file mode 100644 index 0000000..8b61fae --- /dev/null +++ b/content-agent.mdx @@ -0,0 +1,128 @@ +--- +title: 'Content Agent' +description: 'Generate artist videos from Slack with the Recoup Content Agent bot' +--- + +## Overview + +The **Recoup Content Agent** is a Slack bot that generates social-ready artist videos on @mention. It plugs into the existing [content creation pipeline](/api-reference/content/create) and delivers results directly in your Slack thread. + +## How It Works + +``` +User mentions bot in Slack + ↓ +@RecoupContentAgent [template] [batch=N] [lipsync] + ↓ +Bot validates the artist and sends an immediate acknowledgment + ↓ +Triggers the content creation pipeline (POST /api/content/create) + ↓ +Background task polls for completion (~5-10 min) + ↓ +Bot replies in-thread with the generated video URL(s) +``` + +### @Mention Syntax + +``` +@RecoupContentAgent [template] [batch=N] [lipsync] +``` + +| Parameter | Required | Description | +|-----------|----------|-------------| +| `artist_account_id` | Yes | UUID of the artist account | +| `template` | No | Content template name (defaults to `artist-caption-bedroom`) | +| `batch=N` | No | Number of videos to generate (1-30, default 1) | +| `lipsync` | No | Enable lipsync mode (audio baked into video) | + +### Examples + +**Basic — single video with default template:** +``` +@RecoupContentAgent abc-123-uuid +``` + +**Custom template:** +``` +@RecoupContentAgent abc-123-uuid artist-caption-bedroom +``` + +**Batch with lipsync:** +``` +@RecoupContentAgent abc-123-uuid batch=3 lipsync +``` + +## Architecture + +The content agent follows the same pattern as the coding agent bot: + +| Component | Location | Purpose | +|-----------|----------|---------| +| Slack webhook | `POST /api/content-agent/slack` | Receives @mention events | +| Callback endpoint | `POST /api/content-agent/callback` | Receives polling results | +| Bot singleton | `lib/content-agent/bot.ts` | Chat SDK with Slack adapter + Redis state | +| Mention handler | `lib/content-agent/handlers/` | Parses args, validates artist, triggers pipeline | +| Poll task | `poll-content-run` (Trigger.dev) | Monitors content runs, posts results via callback | + +### Data Flow + +1. **Slack event** → `POST /api/content-agent/slack` handles the webhook +2. **Mention handler** parses the command, calls [`GET /api/content/validate`](/api-reference/content/validate) to check artist readiness +3. **Content creation** triggered via [`POST /api/content/create`](/api-reference/content/create) — returns `runIds` +4. **Poll task** (`poll-content-run`) monitors the Trigger.dev runs every 30 seconds (up to 30 minutes) +5. **Callback** → `POST /api/content-agent/callback` receives results and posts video URLs back to the Slack thread + +### API Endpoints Used + +| Endpoint | When | Purpose | +|----------|------|---------| +| [`GET /api/content/validate`](/api-reference/content/validate) | Before triggering | Checks if the artist has required assets | +| [`POST /api/content/create`](/api-reference/content/create) | On mention | Triggers the video generation pipeline | +| [`GET /api/content/templates`](/api-reference/content/templates) | Reference | Lists available content templates | +| `POST /api/content-agent/callback` | After completion | Internal — receives poll results | + +## Setup + +### 1. Create a Slack App + +1. Go to [api.slack.com/apps](https://api.slack.com/apps) and create a new app +2. Under **OAuth & Permissions**, add bot scopes: + - `chat:write` — post messages + - `app_mentions:read` — receive @mention events +3. Under **Event Subscriptions**: + - Enable events + - Set the request URL to `https://recoup-api.vercel.app/api/content-agent/slack` + - Subscribe to `app_mention` bot event +4. Install the app to your workspace + +### 2. Configure Environment Variables + +| Variable | Where | Description | +|----------|-------|-------------| +| `SLACK_CONTENT_BOT_TOKEN` | API (Vercel) | Bot OAuth token (`xoxb-...`) from Slack app | +| `SLACK_CONTENT_SIGNING_SECRET` | API (Vercel) | Signing secret from Slack app **Basic Information** | +| `CONTENT_AGENT_CALLBACK_SECRET` | API + Tasks | Shared secret for callback authentication (generate a random string) | +| `RECOUP_API_BASE_URL` | Tasks (Trigger.dev) | API base URL (e.g., `https://recoup-api.vercel.app`) | + +### 3. Verify + +Mention the bot in any Slack channel where it's been added: + +``` +@RecoupContentAgent +``` + +You should see: +1. An immediate acknowledgment message +2. A video URL reply in the thread after ~5-10 minutes + +## Troubleshooting + +| Issue | Cause | Fix | +|-------|-------|-----| +| No response from bot | Event subscription URL not configured | Check Slack app Event Subscriptions | +| "Artist not found" | Invalid `artist_account_id` | Verify the UUID exists in the platform | +| "No GitHub repository found" | Artist missing repo config | Ensure the artist account has a linked GitHub repo | +| Timeout after 30 min | Pipeline took too long | Check Trigger.dev dashboard for the failed run | +| "Unsupported template" | Invalid template name | Use [`GET /api/content/templates`](/api-reference/content/templates) to list available templates | diff --git a/docs.json b/docs.json index 9361713..475927d 100644 --- a/docs.json +++ b/docs.json @@ -23,6 +23,12 @@ "authentication", "api-reference/sandboxes/create" ] + }, + { + "group": "Agents", + "pages": [ + "content-agent" + ] } ] }, @@ -243,6 +249,13 @@ "api-reference/content/validate", "api-reference/content/estimate" ] + }, + { + "group": "Content Agent", + "pages": [ + "api-reference/content-agent/webhook", + "api-reference/content-agent/callback" + ] } ] } From 2149b60d583c738c9421bfd986877b659179a13b Mon Sep 17 00:00:00 2001 From: Recoup CTO Agent Date: Tue, 24 Mar 2026 19:54:57 +0000 Subject: [PATCH 2/4] =?UTF-8?q?docs:=20fix=20PR=20feedback=20=E2=80=94=20l?= =?UTF-8?q?ink=20callback=20endpoint=20and=20add=20RECOUP=5FAPI=5FKEY?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Link POST /api/content-agent/callback to its API reference page in both the data flow section and endpoints table - Add missing RECOUP_API_KEY to environment variables table Co-Authored-By: Paperclip --- content-agent.mdx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/content-agent.mdx b/content-agent.mdx index 8b61fae..10d4f89 100644 --- a/content-agent.mdx +++ b/content-agent.mdx @@ -71,7 +71,7 @@ The content agent follows the same pattern as the coding agent bot: 2. **Mention handler** parses the command, calls [`GET /api/content/validate`](/api-reference/content/validate) to check artist readiness 3. **Content creation** triggered via [`POST /api/content/create`](/api-reference/content/create) — returns `runIds` 4. **Poll task** (`poll-content-run`) monitors the Trigger.dev runs every 30 seconds (up to 30 minutes) -5. **Callback** → `POST /api/content-agent/callback` receives results and posts video URLs back to the Slack thread +5. **Callback** → [`POST /api/content-agent/callback`](/api-reference/content-agent/callback) receives results and posts video URLs back to the Slack thread ### API Endpoints Used @@ -80,7 +80,7 @@ The content agent follows the same pattern as the coding agent bot: | [`GET /api/content/validate`](/api-reference/content/validate) | Before triggering | Checks if the artist has required assets | | [`POST /api/content/create`](/api-reference/content/create) | On mention | Triggers the video generation pipeline | | [`GET /api/content/templates`](/api-reference/content/templates) | Reference | Lists available content templates | -| `POST /api/content-agent/callback` | After completion | Internal — receives poll results | +| [`POST /api/content-agent/callback`](/api-reference/content-agent/callback) | After completion | Internal — receives poll results | ## Setup @@ -103,6 +103,7 @@ The content agent follows the same pattern as the coding agent bot: | `SLACK_CONTENT_BOT_TOKEN` | API (Vercel) | Bot OAuth token (`xoxb-...`) from Slack app | | `SLACK_CONTENT_SIGNING_SECRET` | API (Vercel) | Signing secret from Slack app **Basic Information** | | `CONTENT_AGENT_CALLBACK_SECRET` | API + Tasks | Shared secret for callback authentication (generate a random string) | +| `RECOUP_API_KEY` | API + Tasks | Recoup API key for authenticating pipeline requests | | `RECOUP_API_BASE_URL` | Tasks (Trigger.dev) | API base URL (e.g., `https://recoup-api.vercel.app`) | ### 3. Verify From 732eb652d2be0f1d7bf4d0327143f20f51307c33 Mon Sep 17 00:00:00 2001 From: Recoup CTO Agent Date: Thu, 26 Mar 2026 12:24:38 +0000 Subject: [PATCH 3/4] docs: add Content Agent Slack tags admin endpoint Add OpenAPI spec and MDX page for GET /api/admins/content/slack, following the existing coding/slack pattern. Response includes video_links instead of pull_requests. Co-Authored-By: Paperclip --- api-reference/admins/content-slack-tags.mdx | 4 + api-reference/openapi.json | 127 ++++++++++++++++++++ docs.json | 3 +- 3 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 api-reference/admins/content-slack-tags.mdx diff --git a/api-reference/admins/content-slack-tags.mdx b/api-reference/admins/content-slack-tags.mdx new file mode 100644 index 0000000..00dcafe --- /dev/null +++ b/api-reference/admins/content-slack-tags.mdx @@ -0,0 +1,4 @@ +--- +title: 'List Content Agent Slack Tags (Admin)' +openapi: 'GET /api/admins/content/slack' +--- diff --git a/api-reference/openapi.json b/api-reference/openapi.json index e1b1489..180451f 100644 --- a/api-reference/openapi.json +++ b/api-reference/openapi.json @@ -4288,6 +4288,133 @@ } } }, + "/api/admins/content/slack": { + "get": { + "description": "Returns a list of Slack mentions of the Recoup Content Agent bot, pulled directly from the Slack API as the source of truth. Each entry includes the tagger's information, the prompt they sent, the timestamp, the channel, and any video link responses. Supports optional time-period filtering. Requires the authenticated account to be a Recoup admin. Authentication via x-api-key or Authorization Bearer token.", + "parameters": [ + { + "name": "period", + "in": "query", + "description": "Time period to filter tags. One of: all (no date filter), daily (last 24 hours), weekly (last 7 days), monthly (last 30 days). Defaults to all.", + "required": false, + "schema": { + "type": "string", + "enum": ["all", "daily", "weekly", "monthly"], + "default": "all" + } + } + ], + "security": [ + { "apiKeyAuth": [] }, + { "bearerAuth": [] } + ], + "responses": { + "200": { + "description": "Slack tag analytics retrieved successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["status", "total", "tags"], + "properties": { + "status": { + "type": "string", + "enum": ["success"], + "description": "Status of the request" + }, + "total": { + "type": "integer", + "description": "Total number of times the Content Agent was tagged in the requested period", + "example": 18 + }, + "tags": { + "type": "array", + "description": "List of Slack tag events", + "items": { + "type": "object", + "required": ["user_id", "user_name", "prompt", "timestamp", "channel_id", "channel_name"], + "properties": { + "user_id": { + "type": "string", + "description": "Slack ID of the person who tagged the agent", + "example": "U012AB3CD" + }, + "user_name": { + "type": "string", + "description": "Display name of the person who tagged the agent", + "example": "Jane Smith" + }, + "user_avatar": { + "type": ["string", "null"], + "description": "URL of the Slack avatar", + "example": "https://avatars.slack-edge.com/..." + }, + "prompt": { + "type": "string", + "description": "The text of the message sent to the agent", + "example": "create a highlight reel for the new single release" + }, + "timestamp": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 timestamp of the tag event", + "example": "2024-01-15T10:30:00.000Z" + }, + "channel_id": { + "type": "string", + "description": "Slack channel ID where the tag occurred", + "example": "C012AB3CD" + }, + "channel_name": { + "type": "string", + "description": "Human-readable name of the Slack channel", + "example": "content-team" + }, + "video_links": { + "type": "array", + "description": "Video URLs generated by the Content Agent in response to this prompt, parsed from bot replies in the Slack thread", + "items": { + "type": "string", + "format": "uri", + "example": "https://recoupable.com/v/abc123" + }, + "example": ["https://recoupable.com/v/abc123"] + } + } + } + } + } + } + } + } + }, + "401": { + "description": "Unauthorized - missing or invalid credentials", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/AccountErrorResponse" } + } + } + }, + "403": { + "description": "Forbidden - authenticated account is not a Recoup admin", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/AccountErrorResponse" } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/AccountErrorResponse" } + } + } + } + } + } + }, "/api/admins/coding/pr": { "get": { "description": "Returns the status (open, closed, or merged) for each provided GitHub pull request URL. Accepts one or more `pull_requests` query parameters containing GitHub PR URLs. Uses the GitHub REST API to check each pull request's state. Requires the authenticated account to be a Recoup admin. Authentication via x-api-key or Authorization Bearer token.", diff --git a/docs.json b/docs.json index 475927d..6a18a44 100644 --- a/docs.json +++ b/docs.json @@ -88,7 +88,8 @@ "api-reference/admins/emails", "api-reference/admins/privy", "api-reference/admins/coding-agent-slack-tags", - "api-reference/admins/coding-pr" + "api-reference/admins/coding-pr", + "api-reference/admins/content-slack-tags" ] }, { From 4b8f1a88ff39b89524580c29ac871007c695058d Mon Sep 17 00:00:00 2001 From: Sweets Sweetman Date: Thu, 26 Mar 2026 12:12:27 -0500 Subject: [PATCH 4/4] docs: update coding/slack endpoint with full response from prod Add total_pull_requests and tags_with_pull_requests fields to match the actual API response. Update description and examples accordingly. Co-Authored-By: Claude Opus 4.6 --- api-reference/openapi.json | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/api-reference/openapi.json b/api-reference/openapi.json index 180451f..c261cda 100644 --- a/api-reference/openapi.json +++ b/api-reference/openapi.json @@ -4163,7 +4163,7 @@ }, "/api/admins/coding/slack": { "get": { - "description": "Returns a list of Slack mentions of the Recoup Coding Agent bot, pulled directly from the Slack API as the source of truth. Each entry includes the tagger's information, the prompt they sent, the timestamp, and the channel. Supports optional time-period filtering. Requires the authenticated account to be a Recoup admin. Authentication via x-api-key or Authorization Bearer token.", + "description": "Returns a list of Slack mentions of the Recoup Coding Agent bot, pulled directly from the Slack API as the source of truth. Each entry includes the tagger's information, the prompt they sent, the timestamp, the channel, and any GitHub pull request URLs opened in response. Also returns aggregate pull request statistics. Supports optional time-period filtering. Requires the authenticated account to be a Recoup admin. Authentication via x-api-key or Authorization Bearer token.", "parameters": [ { "name": "period", @@ -4188,7 +4188,7 @@ "application/json": { "schema": { "type": "object", - "required": ["status", "total", "tags"], + "required": ["status", "total", "total_pull_requests", "tags_with_pull_requests", "tags"], "properties": { "status": { "type": "string", @@ -4198,7 +4198,17 @@ "total": { "type": "integer", "description": "Total number of times the Coding Agent was tagged in the requested period", - "example": 42 + "example": 134 + }, + "total_pull_requests": { + "type": "integer", + "description": "Total number of pull requests opened by the Coding Agent across all tags in the requested period", + "example": 121 + }, + "tags_with_pull_requests": { + "type": "integer", + "description": "Number of tags that resulted in at least one pull request being opened", + "example": 84 }, "tags": { "type": "array", @@ -4249,9 +4259,9 @@ "items": { "type": "string", "format": "uri", - "example": "https://github.com/recoupable-com/api/pull/42" + "example": "https://github.com/recoupable/api/pull/42" }, - "example": ["https://github.com/recoupable-com/api/pull/42"] + "example": ["https://github.com/recoupable/api/pull/42"] } } }