diff --git a/.changeset/fiery-maps-tan.md b/.changeset/fiery-maps-tan.md new file mode 100644 index 0000000000..79826f3d12 --- /dev/null +++ b/.changeset/fiery-maps-tan.md @@ -0,0 +1,12 @@ +--- +--- + +Add call_adcp_agent tool and Claude Skills for full AdCP protocol access. + +This enables clients to execute the full AdCP spec (not just testing API) via: +- `call_adcp_agent` tool: Low-level proxy to any AdCP-compliant sales agent +- Claude Skills: Protocol knowledge for media-buy, signals, and creative + +Skills teach Claude the protocol schemas and workflows; the tool routes to +whatever agent the user specifies. Auth tokens are looked up from saved +agent context or can be provided directly. diff --git a/.gitignore b/.gitignore index 2a7ee9f89e..d2569fc260 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,9 @@ coverage/ # Addie external repo cache .addie-repos/ +# Generated skill schemas (built from dist/schemas) +skills/*/schemas/ + # Stripe webhook secret (dynamically generated) .stripe-webhook-secret test-results/ diff --git a/docs.json b/docs.json index cf2262d30a..a88bdd852c 100644 --- a/docs.json +++ b/docs.json @@ -204,6 +204,9 @@ { "group": "Reference", "pages": [ + "docs/reference/media-buy-quick-reference", + "docs/reference/signals-quick-reference", + "docs/reference/creative-quick-reference", "docs/reference/roadmap", "docs/reference/release-notes", "docs/reference/changelog", diff --git a/docs/reference/creative-quick-reference.mdx b/docs/reference/creative-quick-reference.mdx new file mode 100644 index 0000000000..6fe5ccb9d0 --- /dev/null +++ b/docs/reference/creative-quick-reference.mdx @@ -0,0 +1,285 @@ +--- +title: Creative Quick Reference +description: Compact reference for executing AdCP Creative Protocol tasks. Use this when interacting with creative agents via call_adcp_agent. +keywords: [adcp tasks, creative api, creative agent, build_creative, preview_creative, quick reference] +--- + +Use the `call_adcp_agent` tool to execute these tasks against any AdCP creative agent. + +## Task Overview + +| Task | Purpose | Response Time | +|------|---------|---------------| +| `list_creative_formats` | View format specifications | ~1s | +| `build_creative` | Generate or transform creatives | ~30s-5m | +| `preview_creative` | Get visual previews | ~5s | + +## Typical Workflow + +1. **Discover formats**: `list_creative_formats` to see available format specs +2. **Build creative**: `build_creative` to generate or transform a manifest +3. **Preview**: `preview_creative` to see how it renders +4. **Sync**: Use `sync_creatives` (media-buy task) to traffic the creative + +--- + +## Task Reference + +### list_creative_formats + +Discover creative formats and their specifications. + +```json +{ + "type": "video", + "asset_types": ["image", "text"] +} +``` + +**Key fields:** +- `format_ids` (array, optional): Request specific format IDs +- `type` (string, optional): Filter by type: `video`, `display`, `audio`, `dooh` +- `asset_types` (array, optional): Filter by accepted asset types +- `max_width`, `max_height` (integer, optional): Dimension constraints +- `is_responsive` (boolean, optional): Filter for responsive formats +- `name_search` (string, optional): Search formats by name + +**Response contains:** +- `formats`: Array of format definitions with `format_id`, `name`, `type`, `assets_required`, `renders` +- `creative_agents`: Optional array of other creative agents providing additional formats + +--- + +### build_creative + +Generate a creative from scratch or transform an existing creative to a different format. + +**Pure Generation (from brief):** +```json +{ + "message": "Create a banner promoting our winter sale with a warm, inviting feel", + "target_format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_300x250_generative" + }, + "creative_manifest": { + "format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_300x250_generative" + }, + "assets": { + "promoted_offerings": { + "brand_manifest": { + "url": "https://mybrand.com", + "name": "My Brand", + "colors": { "primary": "#FF5733" } + }, + "inline_offerings": [ + { + "name": "Winter Sale Collection", + "description": "50% off all winter items" + } + ] + } + } + } +} +``` + +**Transformation (resize/reformat):** +```json +{ + "message": "Adapt this leaderboard to a 300x250 banner", + "creative_manifest": { + "format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_728x90" + }, + "assets": { + "banner_image": { + "asset_type": "image", + "url": "https://cdn.mybrand.com/leaderboard.png", + "width": 728, + "height": 90 + }, + "headline": { + "asset_type": "text", + "content": "Spring Sale - 30% Off" + } + } + }, + "target_format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_300x250" + } +} +``` + +**Key fields:** +- `message` (string, optional): Natural language instructions for generation/transformation +- `creative_manifest` (object, optional): Source manifest - minimal for generation, complete for transformation +- `target_format_id` (object, required): Format to generate - `{ agent_url, id }` + +**Response contains:** +- `creative_manifest`: Complete manifest ready for `preview_creative` or `sync_creatives` + +--- + +### preview_creative + +Generate visual previews of creative manifests. + +**Single preview:** +```json +{ + "request_type": "single", + "format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_300x250" + }, + "creative_manifest": { + "format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_300x250" + }, + "assets": { + "banner_image": { + "asset_type": "image", + "url": "https://cdn.example.com/banner.png", + "width": 300, + "height": 250 + } + } + } +} +``` + +**With device variants:** +```json +{ + "request_type": "single", + "format_id": { "agent_url": "...", "id": "native_responsive" }, + "creative_manifest": { }, + "inputs": [ + { "name": "Desktop", "macros": { "DEVICE_TYPE": "desktop" } }, + { "name": "Mobile", "macros": { "DEVICE_TYPE": "mobile" } } + ] +} +``` + +**Batch preview (5-10x faster):** +```json +{ + "request_type": "batch", + "requests": [ + { "format_id": {}, "creative_manifest": { } }, + { "format_id": {}, "creative_manifest": { } } + ] +} +``` + +**Key fields:** +- `request_type` (string, required): `"single"` or `"batch"` +- `format_id` (object, required for single): Format identifier +- `creative_manifest` (object, required): Complete creative manifest +- `inputs` (array, optional): Generate variants with different macros/contexts +- `output_format` (string, optional): `"url"` (default) or `"html"` + +**Response contains:** +- `previews`: Array of preview objects with `preview_url` or `preview_html` +- `expires_at`: When preview URLs expire + +--- + +## Key Concepts + +### Format IDs + +All format references use structured objects: +```json +{ + "format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_300x250" + } +} +``` + +The `agent_url` specifies the creative agent authoritative for this format. + +### Creative Manifests + +Manifests pair format specifications with actual assets: +```json +{ + "format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_300x250" + }, + "assets": { + "banner_image": { + "asset_type": "image", + "url": "https://cdn.example.com/banner.png", + "width": 300, + "height": 250 + }, + "headline": { + "asset_type": "text", + "content": "Shop Now" + }, + "clickthrough_url": { + "asset_type": "url", + "url": "https://brand.com/sale" + } + } +} +``` + +### Asset Types + +Common asset types: +- `image`: Static images (JPEG, PNG, WebP) +- `video`: Video files (MP4, WebM) or VAST tags +- `audio`: Audio files (MP3, M4A) or DAAST tags +- `text`: Headlines, descriptions, CTAs +- `html`: HTML5 creatives or third-party tags +- `javascript`: JavaScript tags +- `url`: Tracking pixels, clickthrough URLs + +### Brand Manifest + +For generative creatives, provide brand context: +```json +{ + "brand_manifest": { + "url": "https://brand.com", + "name": "Brand Name", + "colors": { "primary": "#FF0000", "secondary": "#0000FF" } + } +} +``` + +### Generative vs Transformation + +- **Pure Generation**: Minimal manifest with `promoted_offerings` in assets. Creative agent generates all output assets from scratch. +- **Transformation**: Complete manifest with existing assets. Creative agent adapts to target format, following `message` guidance. + +--- + +## Error Handling + +Common error patterns: + +- **400 Bad Request**: Invalid manifest or format_id +- **404 Not Found**: Format not supported by this agent +- **422 Validation Error**: Manifest doesn't match format requirements + +Error responses include: +```json +{ + "error": { + "code": "INVALID_FORMAT_ID", + "message": "format_id must be a structured object with 'agent_url' and 'id' fields" + } +} +``` diff --git a/docs/reference/media-buy-quick-reference.mdx b/docs/reference/media-buy-quick-reference.mdx new file mode 100644 index 0000000000..595bd8efca --- /dev/null +++ b/docs/reference/media-buy-quick-reference.mdx @@ -0,0 +1,321 @@ +--- +title: Media Buy Quick Reference +description: Compact reference for executing AdCP Media Buy Protocol tasks. Use this when interacting with sales agents via call_adcp_agent. +keywords: [adcp tasks, media buy api, sales agent, get_products, create_media_buy, sync_creatives, quick reference] +--- + +Use the `call_adcp_agent` tool to execute these tasks against any AdCP sales agent. + +## Task Overview + +| Task | Purpose | Response Time | +|------|---------|---------------| +| `get_products` | Discover inventory using natural language | ~60s | +| `list_authorized_properties` | See publisher properties | ~1s | +| `list_creative_formats` | View creative specifications | ~1s | +| `create_media_buy` | Create campaigns | Minutes-Days | +| `update_media_buy` | Modify campaigns | Minutes-Days | +| `sync_creatives` | Upload creative assets | Minutes-Days | +| `list_creatives` | Query creative library | ~1s | +| `get_media_buy_delivery` | Get performance data | ~60s | + +## Typical Workflow + +1. **Discover products**: `get_products` with a natural language brief +2. **Review formats**: `list_creative_formats` to understand creative requirements +3. **Create campaign**: `create_media_buy` with selected products and budget +4. **Upload creatives**: `sync_creatives` to add creative assets +5. **Monitor delivery**: `get_media_buy_delivery` to track performance + +--- + +## Task Reference + +### get_products + +Discover advertising products using natural language briefs. + +```json +{ + "brief": "Looking for premium video inventory for a tech brand targeting developers", + "brand_manifest": { + "url": "https://example.com" + }, + "filters": { + "channels": ["video", "ctv"], + "budget_range": { "min": 5000, "max": 50000 } + } +} +``` + +**Key fields:** +- `brief` (string): Natural language description of campaign requirements +- `brand_manifest` (object): Brand context - can be `{ "url": "https://..." }` or inline manifest +- `filters` (object, optional): Filter by channels, budget, delivery_type, format_types + +**Response contains:** +- `products`: Array of matching products with `product_id`, `name`, `description`, `pricing_options` +- Each product includes `format_ids` (supported creative formats) and `targeting` (available targeting) + +--- + +### list_authorized_properties + +Get the list of publisher properties this agent can sell. + +```json +{} +``` + +No parameters required. + +**Response contains:** +- `publisher_domains`: Array of domain strings the agent is authorized to sell + +--- + +### list_creative_formats + +View supported creative specifications. + +```json +{ + "format_types": ["video", "display"] +} +``` + +**Key fields:** +- `format_types` (array, optional): Filter to specific format categories + +**Response contains:** +- `formats`: Array of format specifications with dimensions, requirements, and asset schemas + +--- + +### create_media_buy + +Create an advertising campaign from selected products. + +```json +{ + "buyer_ref": "campaign-2026-q1-001", + "brand_manifest": { + "url": "https://acme.com", + "name": "Acme Corporation" + }, + "packages": [ + { + "buyer_ref": "pkg-video-001", + "product_id": "premium_video_30s", + "pricing_option_id": "cpm-standard", + "budget": 10000 + } + ], + "start_time": { + "type": "asap" + }, + "end_time": "2026-03-31T23:59:59Z" +} +``` + +**Key fields:** +- `buyer_ref` (string, required): Your unique identifier for this campaign +- `brand_manifest` (object, required): Brand identity - URL or inline manifest +- `packages` (array, required): Products to purchase, each with: + - `buyer_ref`: Your identifier for this package + - `product_id`: From `get_products` response + - `pricing_option_id`: From product's `pricing_options` + - `budget`: Amount in dollars + - `bid_price`: Required for auction pricing + - `targeting_overlay`: Additional targeting constraints + - `creative_ids` or `creatives`: Creative assignments +- `start_time` (object, required): `{ "type": "asap" }` or `{ "type": "scheduled", "datetime": "..." }` +- `end_time` (string, required): ISO 8601 datetime + +**Response contains:** +- `media_buy_id`: The created campaign identifier +- `status`: Current state (often `pending` for async approval) +- `packages`: Created packages with their IDs + +--- + +### update_media_buy + +Modify an existing campaign. + +```json +{ + "media_buy_id": "mb_abc123", + "updates": { + "budget_change": 5000, + "end_time": "2026-04-30T23:59:59Z", + "status": "paused" + } +} +``` + +**Key fields:** +- `media_buy_id` (string, required): The campaign to update +- `updates` (object): Changes to apply - budget_change, end_time, status, targeting, etc. + +--- + +### sync_creatives + +Upload and manage creative assets. + +```json +{ + "creatives": [ + { + "creative_id": "hero_video_30s", + "name": "Brand Hero Video", + "format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "video_standard_30s" + }, + "assets": { + "video": { + "url": "https://cdn.example.com/hero.mp4", + "width": 1920, + "height": 1080, + "duration_ms": 30000 + } + } + } + ], + "assignments": { + "hero_video_30s": ["pkg_001", "pkg_002"] + } +} +``` + +**Key fields:** +- `creatives` (array, required): Creative assets to sync + - `creative_id`: Your unique identifier + - `format_id`: Object with `agent_url` and `id` from format specifications + - `assets`: Asset content (video, image, html, etc.) +- `assignments` (object, optional): Map creative_id to package IDs +- `dry_run` (boolean): Preview changes without applying +- `delete_missing` (boolean): Archive creatives not in this sync + +--- + +### list_creatives + +Query the creative library with filtering. + +```json +{ + "filters": { + "status": ["active"], + "format_types": ["video"] + }, + "limit": 20 +} +``` + +--- + +### get_media_buy_delivery + +Retrieve performance metrics for a campaign. + +```json +{ + "media_buy_id": "mb_abc123", + "granularity": "daily", + "date_range": { + "start": "2026-01-01", + "end": "2026-01-31" + } +} +``` + +**Response contains:** +- `delivery`: Aggregated metrics (impressions, spend, clicks, etc.) +- `by_package`: Breakdown by package +- `timeseries`: Data points over time if granularity specified + +--- + +## Key Concepts + +### Brand Manifest + +Brand context can be provided in two ways: + +**URL reference** (recommended): +```json +{ + "brand_manifest": { + "url": "https://brand.com" + } +} +``` + +**Inline manifest**: +```json +{ + "brand_manifest": { + "name": "Brand Name", + "url": "https://brand.com", + "tagline": "Brand tagline", + "colors": { "primary": "#FF0000" } + } +} +``` + +### Format IDs + +Creative format identifiers are structured objects: +```json +{ + "format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_300x250" + } +} +``` + +The `agent_url` specifies which creative agent defines the format. Use `https://creative.adcontextprotocol.org` for standard IAB formats. + +### Pricing Options + +Products include `pricing_options` array. Each option has: +- `pricing_option_id`: Use this in `create_media_buy` +- `pricing_model`: "cpm", "cpm-auction", "flat-fee", etc. +- `price`: Base price (for fixed pricing) +- `floor`: Minimum bid (for auction) + +For auction pricing, include `bid_price` in your package. + +### Asynchronous Operations + +Operations like `create_media_buy` and `sync_creatives` may require human approval. The response includes: +- `status: "pending"` - Operation awaiting approval +- `task_id` - For tracking async progress + +Poll or use webhooks to check completion status. + +--- + +## Error Handling + +Common error patterns: + +- **400 Bad Request**: Invalid parameters - check required fields +- **401 Unauthorized**: Invalid or missing authentication token +- **404 Not Found**: Invalid product_id, media_buy_id, or creative_id +- **422 Validation Error**: Schema validation failure - check field types + +Error responses include: +```json +{ + "error": { + "code": "VALIDATION_ERROR", + "message": "budget must be greater than 0", + "field": "packages[0].budget" + } +} +``` diff --git a/docs/reference/signals-quick-reference.mdx b/docs/reference/signals-quick-reference.mdx new file mode 100644 index 0000000000..084b7bd974 --- /dev/null +++ b/docs/reference/signals-quick-reference.mdx @@ -0,0 +1,200 @@ +--- +title: Signals Quick Reference +description: Compact reference for executing AdCP Signals Protocol tasks. Use this when interacting with signal agents via call_adcp_agent. +keywords: [adcp tasks, signals api, signal agent, get_signals, activate_signal, audience targeting, quick reference] +--- + +Use the `call_adcp_agent` tool to execute these tasks against any AdCP signal agent. + +## Task Overview + +| Task | Purpose | Response Time | +|------|---------|---------------| +| `get_signals` | Discover signals using natural language | ~60s | +| `activate_signal` | Activate a signal on a platform/agent | Minutes-Hours | + +## Typical Workflow + +1. **Discover signals**: `get_signals` with a natural language description of targeting needs +2. **Review options**: Evaluate signals by coverage, pricing, and deployment status +3. **Activate if needed**: `activate_signal` for signals not yet live on your platform +4. **Use in campaigns**: Reference the activation key in your media buy targeting + +--- + +## Task Reference + +### get_signals + +Discover signals based on natural language description, with deployment status across platforms. + +```json +{ + "signal_spec": "High-income households interested in luxury goods", + "deliver_to": { + "deployments": [ + { + "type": "platform", + "platform": "the-trade-desk", + "account": "agency-123" + } + ], + "countries": ["US"] + }, + "filters": { + "max_cpm": 5.0, + "catalog_types": ["marketplace"] + }, + "max_results": 5 +} +``` + +**Key fields:** +- `signal_spec` (string, required): Natural language description of desired signals +- `deliver_to` (object, required): Where signals will be used + - `deployments` (array): Target platforms/agents with `type`, `platform`/`agent_url`, and optional `account` + - `countries` (array): ISO country codes where signals will be used +- `filters` (object, optional): Filter by `catalog_types`, `data_providers`, `max_cpm`, `min_coverage_percentage` +- `max_results` (number, optional): Limit number of results + +**Deployment types:** +```json +// DSP platform +{ "type": "platform", "platform": "the-trade-desk", "account": "agency-123" } + +// Sales agent +{ "type": "agent", "agent_url": "https://salesagent.example.com" } +``` + +**Response contains:** +- `signals`: Array of matching signals with: + - `signal_agent_segment_id`: Use this in `activate_signal` + - `name`, `description`: Human-readable signal info + - `data_provider`: Source of the signal data + - `coverage_percentage`: Reach relative to agent's population + - `deployments`: Status per platform with `is_live`, `activation_key`, `estimated_activation_duration_minutes` + - `pricing`: CPM and currency + +--- + +### activate_signal + +Activate a signal for use on a specific platform or agent. + +```json +{ + "signal_agent_segment_id": "luxury_auto_intenders", + "deployments": [ + { + "type": "platform", + "platform": "the-trade-desk", + "account": "agency-123-ttd" + } + ] +} +``` + +**Key fields:** +- `signal_agent_segment_id` (string, required): From `get_signals` response +- `deployments` (array, required): Target deployment(s) with `type`, `platform`/`agent_url`, and optional `account` + +**Response contains:** +- `deployments`: Array with activation results per target + - `activation_key`: The key to use for targeting (segment ID or key-value pair) + - `deployed_at`: ISO timestamp when activation completed + - `estimated_activation_duration_minutes`: Time remaining if async +- `errors`: Any warnings or errors encountered + +--- + +## Key Concepts + +### Deployment Targets + +Signals can be activated on two types of targets: + +**DSP Platforms:** +```json +{ + "type": "platform", + "platform": "the-trade-desk", + "account": "agency-123" +} +``` + +**Sales Agents:** +```json +{ + "type": "agent", + "agent_url": "https://wonderstruck.salesagents.com" +} +``` + +### Activation Keys + +When signals are live, the response includes an activation key for targeting: + +**Segment ID format (typical for DSPs):** +```json +{ + "type": "segment_id", + "segment_id": "ttd_segment_12345" +} +``` + +**Key-Value format (typical for sales agents):** +```json +{ + "type": "key_value", + "key": "audience_segment", + "value": "luxury_auto_intenders" +} +``` + +### Signal Types + +- **marketplace**: Licensed from data providers (CPM pricing) +- **custom**: Built for specific principal accounts +- **owned**: Private signals from your own data (no cost) + +### Coverage Percentage + +Indicates signal reach relative to the agent's population: +- 99%: Very broad signal (matches most identifiers) +- 50%: Medium signal +- 1%: Very niche signal + +### Asynchronous Operations + +Signal activation may take time. Check the response: +- `is_live: true` + `activation_key`: Ready to use immediately +- `is_live: false` + `estimated_activation_duration_minutes`: Activation in progress + +Poll or use webhooks to check completion status. + +--- + +## Error Handling + +Common error codes: + +- `SIGNAL_AGENT_SEGMENT_NOT_FOUND`: Invalid signal_agent_segment_id +- `ACTIVATION_FAILED`: Could not activate signal +- `ALREADY_ACTIVATED`: Signal already active on target +- `DEPLOYMENT_UNAUTHORIZED`: Not authorized for platform/account +- `AGENT_NOT_FOUND`: Private agent not visible to this principal +- `AGENT_ACCESS_DENIED`: Not authorized for this signal agent + +Error responses include: +```json +{ + "errors": [ + { + "code": "DEPLOYMENT_UNAUTHORIZED", + "message": "Account not authorized for this data provider", + "field": "deployment.account", + "suggestion": "Contact your account manager to enable access" + } + ] +} +``` diff --git a/scripts/build-schemas.cjs b/scripts/build-schemas.cjs index b915b37b32..f171c2a76e 100644 --- a/scripts/build-schemas.cjs +++ b/scripts/build-schemas.cjs @@ -30,6 +30,7 @@ const { execSync } = require('child_process'); const SOURCE_DIR = path.join(__dirname, '../static/schemas/source'); const DIST_DIR = path.join(__dirname, '../dist/schemas'); const PACKAGE_JSON = path.join(__dirname, '../package.json'); +const SKILLS_DIR = path.join(__dirname, '../skills'); // Parse command line arguments const args = process.argv.slice(2); @@ -295,6 +296,79 @@ async function generateBundledSchemas(sourceDir, bundledDir, version) { return { successCount, errorCount }; } +/** + * Copy schemas from a source directory to a skill schemas directory + * Returns the count of files copied + */ +function copySchemaDir(sourceDir, targetDir) { + if (!fs.existsSync(sourceDir)) { + return 0; + } + + ensureDir(targetDir); + const entries = fs.readdirSync(sourceDir, { withFileTypes: true }); + let count = 0; + + for (const entry of entries) { + if (entry.isFile() && entry.name.endsWith('.json')) { + const sourcePath = path.join(sourceDir, entry.name); + const targetPath = path.join(targetDir, entry.name); + fs.copyFileSync(sourcePath, targetPath); + count++; + } + } + + return count; +} + +/** + * Generate schemas for a single skill + * Returns the count of files copied, or 0 if source doesn't exist + */ +function generateSkillSchema(versionDir, version, protocol, skillName) { + const sourceDir = path.join(versionDir, protocol); + const skillDir = path.join(SKILLS_DIR, skillName, 'schemas'); + + if (!fs.existsSync(sourceDir)) { + return 0; + } + + if (fs.existsSync(skillDir)) { + fs.rmSync(skillDir, { recursive: true, force: true }); + } + ensureDir(skillDir); + + let count = copySchemaDir(sourceDir, skillDir); + count += copySchemaDir(path.join(versionDir, 'core'), path.join(skillDir, 'core')); + count += copySchemaDir(path.join(versionDir, 'enums'), path.join(skillDir, 'enums')); + + console.log(`📚 Generated skill schemas: skills/${skillName}/schemas/ (${count} files from ${version})`); + return count; +} + +/** + * Generate skill schemas from versioned dist schemas + * Copies protocol schemas to skills/{protocol}/schemas/ + */ +function generateSkillSchemas(versionDir, version) { + const skills = [ + { protocol: 'media-buy', skillName: 'adcp-media-buy' }, + { protocol: 'creative', skillName: 'adcp-creative' }, + { protocol: 'signals', skillName: 'adcp-signals' }, + ]; + + let totalCount = 0; + for (const { protocol, skillName } of skills) { + const count = generateSkillSchema(versionDir, version, protocol, skillName); + if (count === 0 && protocol === 'media-buy') { + console.log(` ⚠️ No media-buy schemas found in ${versionDir}`); + } + totalCount += count; + } + + return totalCount; +} + async function main() { const version = getVersion(); const majorVersion = getMajorVersion(version); @@ -354,6 +428,9 @@ async function main() { const latestBundledDir = path.join(latestDir, 'bundled'); await generateBundledSchemas(SOURCE_DIR, latestBundledDir, 'latest'); + // Generate skill schemas from the release version + generateSkillSchemas(versionDir, version); + // Stage the new versioned directory for git commit // This is needed for the changesets workflow to include it in the version commit console.log(`📝 Staging dist/schemas/${version}/ for git commit`); @@ -401,6 +478,9 @@ async function main() { const { successCount, errorCount } = await generateBundledSchemas(SOURCE_DIR, bundledDir, 'latest'); console.log(` ✓ Bundled ${successCount} schemas${errorCount > 0 ? ` (${errorCount} failed)` : ''}`); + // Generate skill schemas from latest + generateSkillSchemas(latestDir, 'latest'); + // Note: Version aliases (v2, v2.5, v1) are handled by HTTP middleware // No symlinks needed - the server rewrites URLs dynamically diff --git a/server/src/addie/mcp/member-tools.ts b/server/src/addie/mcp/member-tools.ts index 84a5d95895..9972fd47b1 100644 --- a/server/src/addie/mcp/member-tools.ts +++ b/server/src/addie/mcp/member-tools.ts @@ -808,6 +808,51 @@ export const MEMBER_TOOLS: AddieTool[] = [ required: ['agent_url'], }, }, + { + name: 'call_adcp_agent', + description: + 'Execute an AdCP protocol task on an agent. This is a low-level proxy that forwards requests directly to the agent. Use adcp-media-buy, adcp-signals, or adcp-creative skills for detailed parameter schemas.', + usage_hints: 'use for "call get_products", "execute create_media_buy", "run sync_creatives", "get_signals", "build_creative", "interact with the agent directly", "use the full AdCP spec". For testing workflows, prefer test_adcp_agent instead.', + input_schema: { + type: 'object', + properties: { + agent_url: { + type: 'string', + description: 'The agent URL (must be HTTPS, e.g., "https://sales.example.com")', + }, + task: { + type: 'string', + enum: [ + // Media Buy tasks + 'get_products', + 'list_authorized_properties', + 'list_creative_formats', + 'create_media_buy', + 'update_media_buy', + 'sync_creatives', + 'list_creatives', + 'get_media_buy_delivery', + // Signals tasks + 'get_signals', + 'activate_signal', + // Creative tasks + 'build_creative', + 'preview_creative', + ], + description: 'The AdCP task to execute', + }, + params: { + type: 'object', + description: 'Task parameters - structure depends on the task. See protocol skills for schemas.', + }, + auth_token: { + type: 'string', + description: 'Optional auth token. If not provided, will use saved token for this agent.', + }, + }, + required: ['agent_url', 'task'], + }, + }, // ============================================ // AGENT CONTEXT MANAGEMENT // ============================================ @@ -2506,6 +2551,87 @@ export function createMemberToolHandlers( } }); + // ============================================ + // ADCP AGENT PROXY + // ============================================ + handlers.set('call_adcp_agent', async (input) => { + const agentUrl = input.agent_url as string; + const task = input.task as string; + const params = (input.params as Record) || {}; + let authToken = input.auth_token as string | undefined; + + // Validate URL to prevent SSRF attacks + try { + const url = new URL(agentUrl); + + // Must be HTTPS + if (url.protocol !== 'https:') { + return '**Error:** Agent URL must use HTTPS protocol.'; + } + + // Block internal/private networks + const hostname = url.hostname.toLowerCase(); + if ( + hostname === 'localhost' || + hostname === '127.0.0.1' || + hostname === '::1' || + hostname.endsWith('.local') || + hostname.endsWith('.internal') || + hostname.startsWith('10.') || + hostname.startsWith('192.168.') || + hostname.match(/^172\.(1[6-9]|2\d|3[01])\./) || + hostname === '169.254.169.254' // AWS metadata + ) { + return '**Error:** Agent URL cannot point to internal or private networks.'; + } + } catch { + return '**Error:** Invalid agent URL format.'; + } + + // Look up saved auth token if not provided + if (!authToken && memberContext?.organization?.workos_organization_id) { + const savedContext = await agentContextDb.getByOrgAndUrl( + memberContext.organization.workos_organization_id, + agentUrl + ); + if (savedContext?.id) { + authToken = (await agentContextDb.getAuthToken(savedContext.id)) ?? undefined; + } + } + + logger.info({ agentUrl, task, hasAuth: !!authToken }, 'Addie: call_adcp_agent'); + + try { + const { AdCPClient } = await import('@adcp/client'); + const multiClient = new AdCPClient([ + { + id: 'target', + name: 'target', + agent_uri: agentUrl, + protocol: 'mcp', + ...(authToken && { auth_token: authToken }), + }, + ]); + const client = multiClient.agent('target'); + + const result = await client.executeTask(task, params); + + if (!result.success) { + // Return errors in a structured way + return `**Task failed:** \`${task}\`\n\n**Error:**\n\`\`\`json\n${JSON.stringify(result.error, null, 2)}\n\`\`\``; + } + + // Format successful response + let output = `**Task:** \`${task}\`\n**Status:** Success\n\n`; + output += `**Response:**\n\`\`\`json\n${JSON.stringify(result.data, null, 2)}\n\`\`\``; + + return output; + } catch (error) { + logger.error({ error, agentUrl, task }, 'Addie: call_adcp_agent failed'); + return `**Task failed:** \`${task}\`\n\n**Error:** ${error instanceof Error ? error.message : 'Unknown error'}`; + } + }); + // ============================================ // GITHUB ISSUE DRAFTING // ============================================ diff --git a/skills/adcp-creative/SKILL.md b/skills/adcp-creative/SKILL.md new file mode 100644 index 0000000000..aff9b228aa --- /dev/null +++ b/skills/adcp-creative/SKILL.md @@ -0,0 +1,289 @@ +--- +name: adcp-creative +description: Execute AdCP Creative Protocol operations with creative agents - build creatives from briefs or existing assets, preview renderings, and discover format specifications. Use when users want to generate or transform ad creatives, preview how ads will look, or understand creative format requirements. +--- + +# AdCP Creative Protocol + +This skill enables you to execute the AdCP Creative Protocol with creative agents. Use the `call_adcp_agent` tool to send requests to creative agents. + +## Overview + +The Creative Protocol provides 3 standardized tasks for building and previewing advertising creatives: + +| Task | Purpose | Response Time | +|------|---------|---------------| +| `list_creative_formats` | View format specifications | ~1s | +| `build_creative` | Generate or transform creatives | ~30s-5m | +| `preview_creative` | Get visual previews | ~5s | + +## Typical Workflow + +1. **Discover formats**: `list_creative_formats` to see available format specs +2. **Build creative**: `build_creative` to generate or transform a manifest +3. **Preview**: `preview_creative` to see how it renders +4. **Sync**: Use `sync_creatives` (media-buy task) to traffic the creative + +--- + +## Task Reference + +### list_creative_formats + +Discover creative formats and their specifications. + +**Request:** +```json +{ + "type": "video", + "asset_types": ["image", "text"] +} +``` + +**Key fields:** +- `format_ids` (array, optional): Request specific format IDs +- `type` (string, optional): Filter by type: `video`, `display`, `audio`, `dooh` +- `asset_types` (array, optional): Filter by accepted asset types +- `max_width`, `max_height` (integer, optional): Dimension constraints +- `is_responsive` (boolean, optional): Filter for responsive formats +- `name_search` (string, optional): Search formats by name + +**Response contains:** +- `formats`: Array of format definitions with `format_id`, `name`, `type`, `assets_required`, `renders` +- `creative_agents`: Optional array of other creative agents providing additional formats + +--- + +### build_creative + +Generate a creative from scratch or transform an existing creative to a different format. + +**Pure Generation (from brief):** +```json +{ + "message": "Create a banner promoting our winter sale with a warm, inviting feel", + "target_format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_300x250_generative" + }, + "creative_manifest": { + "format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_300x250_generative" + }, + "assets": { + "promoted_offerings": { + "brand_manifest": { + "url": "https://mybrand.com", + "name": "My Brand", + "colors": { "primary": "#FF5733" } + }, + "inline_offerings": [ + { + "name": "Winter Sale Collection", + "description": "50% off all winter items" + } + ] + } + } + } +} +``` + +**Transformation (resize/reformat):** +```json +{ + "message": "Adapt this leaderboard to a 300x250 banner", + "creative_manifest": { + "format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_728x90" + }, + "assets": { + "banner_image": { + "asset_type": "image", + "url": "https://cdn.mybrand.com/leaderboard.png", + "width": 728, + "height": 90 + }, + "headline": { + "asset_type": "text", + "content": "Spring Sale - 30% Off" + } + } + }, + "target_format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_300x250" + } +} +``` + +**Key fields:** +- `message` (string, optional): Natural language instructions for generation/transformation +- `creative_manifest` (object, optional): Source manifest - minimal for generation, complete for transformation +- `target_format_id` (object, required): Format to generate - `{ agent_url, id }` + +**Response contains:** +- `creative_manifest`: Complete manifest ready for `preview_creative` or `sync_creatives` + +--- + +### preview_creative + +Generate visual previews of creative manifests. + +**Single preview:** +```json +{ + "request_type": "single", + "format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_300x250" + }, + "creative_manifest": { + "format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_300x250" + }, + "assets": { + "banner_image": { + "asset_type": "image", + "url": "https://cdn.example.com/banner.png", + "width": 300, + "height": 250 + } + } + } +} +``` + +**With device variants:** +```json +{ + "request_type": "single", + "format_id": { "agent_url": "...", "id": "native_responsive" }, + "creative_manifest": { /* ... */ }, + "inputs": [ + { "name": "Desktop", "macros": { "DEVICE_TYPE": "desktop" } }, + { "name": "Mobile", "macros": { "DEVICE_TYPE": "mobile" } } + ] +} +``` + +**Batch preview (5-10x faster):** +```json +{ + "request_type": "batch", + "requests": [ + { "format_id": {...}, "creative_manifest": { /* creative 1 */ } }, + { "format_id": {...}, "creative_manifest": { /* creative 2 */ } } + ] +} +``` + +**Key fields:** +- `request_type` (string, required): `"single"` or `"batch"` +- `format_id` (object, required for single): Format identifier +- `creative_manifest` (object, required): Complete creative manifest +- `inputs` (array, optional): Generate variants with different macros/contexts +- `output_format` (string, optional): `"url"` (default) or `"html"` + +**Response contains:** +- `previews`: Array of preview objects with `preview_url` or `preview_html` +- `expires_at`: When preview URLs expire + +--- + +## Key Concepts + +### Format IDs + +All format references use structured objects: +```json +{ + "format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_300x250" + } +} +``` + +The `agent_url` specifies the creative agent authoritative for this format. + +### Creative Manifests + +Manifests pair format specifications with actual assets: +```json +{ + "format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_300x250" + }, + "assets": { + "banner_image": { + "asset_type": "image", + "url": "https://cdn.example.com/banner.png", + "width": 300, + "height": 250 + }, + "headline": { + "asset_type": "text", + "content": "Shop Now" + }, + "clickthrough_url": { + "asset_type": "url", + "url": "https://brand.com/sale" + } + } +} +``` + +### Asset Types + +Common asset types: +- `image`: Static images (JPEG, PNG, WebP) +- `video`: Video files (MP4, WebM) or VAST tags +- `audio`: Audio files (MP3, M4A) or DAAST tags +- `text`: Headlines, descriptions, CTAs +- `html`: HTML5 creatives or third-party tags +- `javascript`: JavaScript tags +- `url`: Tracking pixels, clickthrough URLs + +### Brand Manifest + +For generative creatives, provide brand context: +```json +{ + "brand_manifest": { + "url": "https://brand.com", + "name": "Brand Name", + "colors": { "primary": "#FF0000", "secondary": "#0000FF" } + } +} +``` + +### Generative vs Transformation + +- **Pure Generation**: Minimal manifest with `promoted_offerings` in assets. Creative agent generates all output assets from scratch. +- **Transformation**: Complete manifest with existing assets. Creative agent adapts to target format, following `message` guidance. + +--- + +## Error Handling + +Common error patterns: + +- **400 Bad Request**: Invalid manifest or format_id +- **404 Not Found**: Format not supported by this agent +- **422 Validation Error**: Manifest doesn't match format requirements + +Error responses include: +```json +{ + "error": { + "code": "INVALID_FORMAT_ID", + "message": "format_id must be a structured object with 'agent_url' and 'id' fields" + } +} +``` diff --git a/skills/adcp-media-buy/SKILL.md b/skills/adcp-media-buy/SKILL.md new file mode 100644 index 0000000000..80e3072609 --- /dev/null +++ b/skills/adcp-media-buy/SKILL.md @@ -0,0 +1,345 @@ +--- +name: adcp-media-buy +description: Execute AdCP Media Buy Protocol operations with sales agents - discover advertising products, create and manage campaigns, sync creatives, and track delivery. Use when users want to buy advertising, create media buys, interact with ad sales agents, or test advertising APIs. +--- + +# AdCP Media Buy Protocol + +This skill enables you to execute the AdCP Media Buy Protocol with sales agents. Use the `call_adcp_agent` tool to send requests to sales agents. + +## Overview + +The Media Buy Protocol provides 8 standardized tasks for managing advertising campaigns: + +| Task | Purpose | Response Time | +|------|---------|---------------| +| `get_products` | Discover inventory using natural language | ~60s | +| `list_authorized_properties` | See publisher properties | ~1s | +| `list_creative_formats` | View creative specifications | ~1s | +| `create_media_buy` | Create campaigns | Minutes-Days | +| `update_media_buy` | Modify campaigns | Minutes-Days | +| `sync_creatives` | Upload creative assets | Minutes-Days | +| `list_creatives` | Query creative library | ~1s | +| `get_media_buy_delivery` | Get performance data | ~60s | + +## Typical Workflow + +1. **Discover products**: `get_products` with a natural language brief +2. **Review formats**: `list_creative_formats` to understand creative requirements +3. **Create campaign**: `create_media_buy` with selected products and budget +4. **Upload creatives**: `sync_creatives` to add creative assets +5. **Monitor delivery**: `get_media_buy_delivery` to track performance + +--- + +## Task Reference + +### get_products + +Discover advertising products using natural language briefs. + +**Request:** +```json +{ + "brief": "Looking for premium video inventory for a tech brand targeting developers", + "brand_manifest": { + "url": "https://example.com" + }, + "filters": { + "channels": ["video", "ctv"], + "budget_range": { "min": 5000, "max": 50000 } + } +} +``` + +**Key fields:** +- `brief` (string): Natural language description of campaign requirements +- `brand_manifest` (object): Brand context - can be `{ "url": "https://..." }` or inline manifest +- `filters` (object, optional): Filter by channels, budget, delivery_type, format_types + +**Response contains:** +- `products`: Array of matching products with `product_id`, `name`, `description`, `pricing_options` +- Each product includes `format_ids` (supported creative formats) and `targeting` (available targeting) + +--- + +### list_authorized_properties + +Get the list of publisher properties this agent can sell. + +**Request:** +```json +{} +``` + +No parameters required. + +**Response contains:** +- `publisher_domains`: Array of domain strings the agent is authorized to sell + +--- + +### list_creative_formats + +View supported creative specifications. + +**Request:** +```json +{ + "format_types": ["video", "display"] +} +``` + +**Key fields:** +- `format_types` (array, optional): Filter to specific format categories + +**Response contains:** +- `formats`: Array of format specifications with dimensions, requirements, and asset schemas + +--- + +### create_media_buy + +Create an advertising campaign from selected products. + +**Request:** +```json +{ + "buyer_ref": "campaign-2024-q1-001", + "brand_manifest": { + "url": "https://acme.com", + "name": "Acme Corporation" + }, + "packages": [ + { + "buyer_ref": "pkg-video-001", + "product_id": "premium_video_30s", + "pricing_option_id": "cpm-standard", + "budget": 10000 + } + ], + "start_time": { + "type": "asap" + }, + "end_time": "2024-03-31T23:59:59Z" +} +``` + +**Key fields:** +- `buyer_ref` (string, required): Your unique identifier for this campaign +- `brand_manifest` (object, required): Brand identity - URL or inline manifest +- `packages` (array, required): Products to purchase, each with: + - `buyer_ref`: Your identifier for this package + - `product_id`: From `get_products` response + - `pricing_option_id`: From product's `pricing_options` + - `budget`: Amount in dollars + - `bid_price`: Required for auction pricing + - `targeting_overlay`: Additional targeting constraints + - `creative_ids` or `creatives`: Creative assignments +- `start_time` (object, required): `{ "type": "asap" }` or `{ "type": "scheduled", "datetime": "..." }` +- `end_time` (string, required): ISO 8601 datetime + +**Response contains:** +- `media_buy_id`: The created campaign identifier +- `status`: Current state (often `pending` for async approval) +- `packages`: Created packages with their IDs + +--- + +### update_media_buy + +Modify an existing campaign. + +**Request:** +```json +{ + "media_buy_id": "mb_abc123", + "updates": { + "budget_change": 5000, + "end_time": "2024-04-30T23:59:59Z", + "status": "paused" + } +} +``` + +**Key fields:** +- `media_buy_id` (string, required): The campaign to update +- `updates` (object): Changes to apply - budget_change, end_time, status, targeting, etc. + +--- + +### sync_creatives + +Upload and manage creative assets. + +**Request:** +```json +{ + "creatives": [ + { + "creative_id": "hero_video_30s", + "name": "Brand Hero Video", + "format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "video_standard_30s" + }, + "assets": { + "video": { + "url": "https://cdn.example.com/hero.mp4", + "width": 1920, + "height": 1080, + "duration_ms": 30000 + } + } + } + ], + "assignments": { + "hero_video_30s": ["pkg_001", "pkg_002"] + } +} +``` + +**Key fields:** +- `creatives` (array, required): Creative assets to sync + - `creative_id`: Your unique identifier + - `format_id`: Object with `agent_url` and `id` from format specifications + - `assets`: Asset content (video, image, html, etc.) +- `assignments` (object, optional): Map creative_id to package IDs +- `dry_run` (boolean): Preview changes without applying +- `delete_missing` (boolean): Archive creatives not in this sync + +--- + +### list_creatives + +Query the creative library with filtering. + +**Request:** +```json +{ + "filters": { + "status": ["active"], + "format_types": ["video"] + }, + "limit": 20 +} +``` + +--- + +### get_media_buy_delivery + +Retrieve performance metrics for a campaign. + +**Request:** +```json +{ + "media_buy_id": "mb_abc123", + "granularity": "daily", + "date_range": { + "start": "2024-01-01", + "end": "2024-01-31" + } +} +``` + +**Response contains:** +- `delivery`: Aggregated metrics (impressions, spend, clicks, etc.) +- `by_package`: Breakdown by package +- `timeseries`: Data points over time if granularity specified + +--- + +## Key Concepts + +### Brand Manifest + +Brand context can be provided in two ways: + +1. **URL reference** (recommended): +```json +{ + "brand_manifest": { + "url": "https://brand.com" + } +} +``` + +2. **Inline manifest**: +```json +{ + "brand_manifest": { + "name": "Brand Name", + "url": "https://brand.com", + "tagline": "Brand tagline", + "colors": { "primary": "#FF0000" } + } +} +``` + +### Format IDs + +Creative format identifiers are structured objects: +```json +{ + "format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_300x250" + } +} +``` + +The `agent_url` specifies which creative agent defines the format. Use `https://creative.adcontextprotocol.org` for standard IAB formats. + +### Pricing Options + +Products include `pricing_options` array. Each option has: +- `pricing_option_id`: Use this in `create_media_buy` +- `pricing_model`: "cpm", "cpm-auction", "flat-fee", etc. +- `price`: Base price (for fixed pricing) +- `floor`: Minimum bid (for auction) + +For auction pricing, include `bid_price` in your package. + +### Asynchronous Operations + +Operations like `create_media_buy` and `sync_creatives` may require human approval. The response includes: +- `status: "pending"` - Operation awaiting approval +- `task_id` - For tracking async progress + +Poll or use webhooks to check completion status. + +--- + +## Error Handling + +Common error patterns: + +- **400 Bad Request**: Invalid parameters - check required fields +- **401 Unauthorized**: Invalid or missing authentication token +- **404 Not Found**: Invalid product_id, media_buy_id, or creative_id +- **422 Validation Error**: Schema validation failure - check field types + +Error responses include: +```json +{ + "errors": [ + { + "code": "VALIDATION_ERROR", + "message": "budget must be greater than 0", + "field": "packages[0].budget" + } + ] +} +``` + +--- + +## Testing Mode + +For testing without real transactions, agents may support: +- `X-Dry-Run: true` header - Preview without side effects +- Test products with `test: true` flag +- Sandbox environments + +Ask the agent about testing capabilities before creating real campaigns. diff --git a/skills/adcp-signals/SKILL.md b/skills/adcp-signals/SKILL.md new file mode 100644 index 0000000000..d35159dfd2 --- /dev/null +++ b/skills/adcp-signals/SKILL.md @@ -0,0 +1,205 @@ +--- +name: adcp-signals +description: Execute AdCP Signals Protocol operations with signal agents - discover audience signals using natural language and activate them on DSPs or sales agents. Use when users want to find targeting data, activate audience segments, or work with signal providers. +--- + +# AdCP Signals Protocol + +This skill enables you to execute the AdCP Signals Protocol with signal agents. Use the `call_adcp_agent` tool to send requests to signal agents. + +## Overview + +The Signals Protocol provides 2 standardized tasks for discovering and activating targeting data: + +| Task | Purpose | Response Time | +|------|---------|---------------| +| `get_signals` | Discover signals using natural language | ~60s | +| `activate_signal` | Activate a signal on a platform/agent | Minutes-Hours | + +## Typical Workflow + +1. **Discover signals**: `get_signals` with a natural language description of targeting needs +2. **Review options**: Evaluate signals by coverage, pricing, and deployment status +3. **Activate if needed**: `activate_signal` for signals not yet live on your platform +4. **Use in campaigns**: Reference the activation key in your media buy targeting + +--- + +## Task Reference + +### get_signals + +Discover signals based on natural language description, with deployment status across platforms. + +**Request:** +```json +{ + "signal_spec": "High-income households interested in luxury goods", + "deliver_to": { + "deployments": [ + { + "type": "platform", + "platform": "the-trade-desk", + "account": "agency-123" + } + ], + "countries": ["US"] + }, + "filters": { + "max_cpm": 5.0, + "catalog_types": ["marketplace"] + }, + "max_results": 5 +} +``` + +**Key fields:** +- `signal_spec` (string, required): Natural language description of desired signals +- `deliver_to` (object, required): Where signals will be used + - `deployments` (array): Target platforms/agents with `type`, `platform`/`agent_url`, and optional `account` + - `countries` (array): ISO country codes where signals will be used +- `filters` (object, optional): Filter by `catalog_types`, `data_providers`, `max_cpm`, `min_coverage_percentage` +- `max_results` (number, optional): Limit number of results + +**Deployment types:** +```json +// DSP platform +{ "type": "platform", "platform": "the-trade-desk", "account": "agency-123" } + +// Sales agent +{ "type": "agent", "agent_url": "https://salesagent.example.com" } +``` + +**Response contains:** +- `signals`: Array of matching signals with: + - `signal_agent_segment_id`: Use this in `activate_signal` + - `name`, `description`: Human-readable signal info + - `data_provider`: Source of the signal data + - `coverage_percentage`: Reach relative to agent's population + - `deployments`: Status per platform with `is_live`, `activation_key`, `estimated_activation_duration_minutes` + - `pricing`: CPM and currency + +--- + +### activate_signal + +Activate a signal for use on a specific platform or agent. + +**Request:** +```json +{ + "signal_agent_segment_id": "luxury_auto_intenders", + "deployments": [ + { + "type": "platform", + "platform": "the-trade-desk", + "account": "agency-123-ttd" + } + ] +} +``` + +**Key fields:** +- `signal_agent_segment_id` (string, required): From `get_signals` response +- `deployments` (array, required): Target deployment(s) with `type`, `platform`/`agent_url`, and optional `account` + +**Response contains:** +- `deployments`: Array with activation results per target + - `activation_key`: The key to use for targeting (segment ID or key-value pair) + - `deployed_at`: ISO timestamp when activation completed + - `estimated_activation_duration_minutes`: Time remaining if async +- `errors`: Any warnings or errors encountered + +--- + +## Key Concepts + +### Deployment Targets + +Signals can be activated on two types of targets: + +**DSP Platforms:** +```json +{ + "type": "platform", + "platform": "the-trade-desk", + "account": "agency-123" +} +``` + +**Sales Agents:** +```json +{ + "type": "agent", + "agent_url": "https://wonderstruck.salesagents.com" +} +``` + +### Activation Keys + +When signals are live, the response includes an activation key for targeting: + +**Segment ID format (typical for DSPs):** +```json +{ + "type": "segment_id", + "segment_id": "ttd_segment_12345" +} +``` + +**Key-Value format (typical for sales agents):** +```json +{ + "type": "key_value", + "key": "audience_segment", + "value": "luxury_auto_intenders" +} +``` + +### Signal Types + +- **marketplace**: Licensed from data providers (CPM pricing) +- **custom**: Built for specific principal accounts +- **owned**: Private signals from your own data (no cost) + +### Coverage Percentage + +Indicates signal reach relative to the agent's population: +- 99%: Very broad signal (matches most identifiers) +- 50%: Medium signal +- 1%: Very niche signal + +### Asynchronous Operations + +Signal activation may take time. Check the response: +- `is_live: true` + `activation_key`: Ready to use immediately +- `is_live: false` + `estimated_activation_duration_minutes`: Activation in progress + +Poll or use webhooks to check completion status. + +--- + +## Error Handling + +Common error codes: + +- `SIGNAL_AGENT_SEGMENT_NOT_FOUND`: Invalid signal_agent_segment_id +- `ACTIVATION_FAILED`: Could not activate signal +- `ALREADY_ACTIVATED`: Signal already active on target +- `DEPLOYMENT_UNAUTHORIZED`: Not authorized for platform/account +- `AGENT_NOT_FOUND`: Private agent not visible to this principal +- `AGENT_ACCESS_DENIED`: Not authorized for this signal agent + +Error responses include: +```json +{ + "errors": [ + { + "code": "DEPLOYMENT_UNAUTHORIZED", + "message": "Account not authorized for this data provider", + "field": "deployment.account", + "suggestion": "Contact your account manager to enable access" + } + ] +} +```