-
Notifications
You must be signed in to change notification settings - Fork 2
docs: Recoup Content Agent guide and API reference #78
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| --- | ||
| title: 'Task Callback' | ||
| openapi: 'POST /api/content-agent/callback' | ||
| --- |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| --- | ||
| title: 'Slack Webhook' | ||
| openapi: 'POST /api/content-agent/{platform}' | ||
| --- |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 `<artist_account_id> [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" | ||
| } | ||
| } | ||
| } | ||
| }, | ||
|
Comment on lines
+4504
to
+4552
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # First, let's check the structure of the openapi.json file and find the global security definition
head -100 api-reference/openapi.json | grep -A 10 '"security"' || echo "Checking file structure..."Repository: recoupable/docs Length of output: 441 🏁 Script executed: # Get the full context around lines 4504-4552 to see the endpoint and any security override
sed -n '4490,4560p' api-reference/openapi.jsonRepository: recoupable/docs Length of output: 2833 🏁 Script executed: # Search for the top-level security definition to confirm global apiKeyAuth
rg '"security"\s*:' api-reference/openapi.json -B 5 -A 10 | head -80Repository: recoupable/docs Length of output: 2850 🏁 Script executed: # Check if there are other webhook or public endpoints with "security": [] override
rg '"security"\s*:\s*\[\]' api-reference/openapi.json -B 15 -A 5Repository: recoupable/docs Length of output: 41 Webhook endpoint incorrectly inherits global The Required fix "/api/content-agent/{platform}": {
"post": {
+ "security": [],
"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 `<artist_account_id> [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.",🤖 Prompt for AI Agents |
||
| "/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": [] | ||
| } | ||
| ] | ||
|
Comment on lines
+4637
to
+4641
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
python - <<'PY'
import json, sys
with open("api-reference/openapi.json", "r", encoding="utf-8") as f:
doc = json.load(f)
schemes = set(doc.get("components", {}).get("securitySchemes", {}).keys())
missing = []
for path, item in doc.get("paths", {}).items():
for method, op in item.items():
if method.lower() not in {"get","post","put","patch","delete","head","options","trace"}:
continue
for req in op.get("security", []):
for name in req.keys():
if name not in schemes:
missing.append((path, method.upper(), name))
if missing:
print("Missing security scheme definitions:")
for m in missing:
print(f"- {m[1]} {m[0]} -> {m[2]}")
sys.exit(1)
print("All referenced security schemes are defined.")
PYRepository: recoupable/docs Length of output: 148 Add missing The 🔧 Proposed fix@@
"securitySchemes": {
+ "callbackSecret": {
+ "type": "apiKey",
+ "in": "header",
+ "name": "x-callback-secret",
+ "description": "Shared secret for internal content-agent callback authentication."
+ },
"bearerAuth": {
"type": "http",
"scheme": "bearer"
},🤖 Prompt for AI Agents |
||
| } | ||
| } | ||
| }, | ||
| "components": { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,129 @@ | ||
| --- | ||
| 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 <artist_account_id> [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 <artist_account_id> [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 | ||
| ``` | ||
|
Comment on lines
+26
to
+54
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider documenting all ContentCreateRequest parameters. The If these parameters are intentionally omitted from the bot interface, consider adding a note explaining that the bot provides a simplified interface. Otherwise, document how users can pass these additional options. 🤖 Prompt for AI Agents |
||
|
|
||
| ## 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 | | ||
|
Comment on lines
+56
to
+66
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Align webhook endpoint with OpenAPI path parameter. The Architecture table shows
This ensures consistency between the guide and the API reference. 🤖 Prompt for AI Agents |
||
|
|
||
| ### 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`](/api-reference/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`](/api-reference/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 | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is recoup Api key not listed here? |
||
|
|
||
| | 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_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 | ||
|
|
||
| Mention the bot in any Slack channel where it's been added: | ||
|
|
||
| ``` | ||
| @RecoupContentAgent <your-artist-account-id> | ||
| ``` | ||
|
|
||
| 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 | | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
Slack Events API url_verification response format (what exact JSON/body is required for challenge acknowledgement?)💡 Result:
The Slack Events API sends a url_verification challenge during Request URL configuration. Respond with HTTP 200 OK and the exact challenge value from the incoming JSON payload. Supported response formats (any one works): 1. Plaintext (recommended): Content-Type: text/plain Body: 3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P 2. Form-urlencoded: Content-Type: application/x-www-form-urlencoded Body: challenge=3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P 3. JSON: Content-Type: application/json Body: {"challenge":"3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P"} Incoming payload example: { "token": "Jhj5dZrVaK7ZwHHjRyZWjbDl", "challenge": "3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P", "type": "url_verification" } Respond immediately with the challenge value (no JSON wrapper required). Verify request authenticity using signing secrets for production events.
Citations:
🏁 Script executed:
Repository: recoupable/docs
Length of output: 3964
🏁 Script executed:
Repository: recoupable/docs
Length of output: 651
Response schema must document the
url_verificationchallenge response.The endpoint explicitly handles Slack
url_verificationchallenges during app setup (per the description), but the 200 response schema only documents{ ok: boolean }. According to Slack's Events API,url_verificationrequests require responding with the challenge value in JSON format:{"challenge": "<value>"}.Update the response schema to use
oneOfto cover both response cases: thechallengeresponse forurl_verificationevents and theokresponse for regular events.Proposed fix
"200": { "description": "Event processed successfully", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "ok": { - "type": "boolean" - } - } + "oneOf": [ + { + "type": "object", + "required": ["challenge"], + "properties": { + "challenge": { "type": "string" } + } + }, + { + "type": "object", + "properties": { + "ok": { "type": "boolean" } + } + } + ] } } } }📝 Committable suggestion
🤖 Prompt for AI Agents