diff --git a/productclank/SKILL.md b/productclank/SKILL.md new file mode 100644 index 00000000..dc1c180a --- /dev/null +++ b/productclank/SKILL.md @@ -0,0 +1,219 @@ +--- +name: productclank-campaigns +description: This skill should be used when the user asks to "create a Twitter campaign", "boost a tweet", "amplify a post", "run community-driven marketing", "intercept competitor mentions", "scale word-of-mouth", "turn users into brand advocates", or mentions "ProductClank", "Communiply", or "Twitter engagement campaign". Enables AI-powered brand advocacy on Twitter/X through community coordination. +license: Proprietary +metadata: + author: ProductClank + version: "3.0.0" + api_endpoint: https://api.productclank.com/api/v1/agents/campaigns + website: https://www.productclank.com + web_ui: https://app.productclank.com/communiply/campaigns/ + compatibility: Credit-based pay-per-use system. Self-registration with 300 free credits. Agents buy more credits with USDC on Base (chain ID 8453). Supports x402 protocol and direct USDC transfers. Works with any wallet type. +--- + +# ProductClank Communiply — Community-Driven Brand Advocacy + +Communiply solves the authenticity problem in social media marketing. Instead of a brand promoting itself (which people dismiss as advertising), real community members naturally recommend the brand in relevant Twitter/X conversations — creating genuine word-of-mouth at scale. + +**How it works:** AI discovers relevant conversations 24/7 → generates context-aware replies → community members post from personal accounts → track engagement and ROI. + +## Choosing the Right Endpoint + +``` +What to do? +│ +├─ Amplify a specific tweet RIGHT NOW +│ └─> POST /agents/campaigns/boost +│ Cost: 200-300 credits | Immediate | One-shot +│ +└─ Monitor conversations ongoing & respond when relevant + └─> POST /agents/campaigns (Communiply) + Cost: 10 + (12 × posts) | Ongoing 24/7 | Keyword-based +``` + +| Feature | Communiply | Boost | +|---------|-----------|-------| +| **Target** | Keyword-based conversations | Specific tweet URL | +| **Cost** | 10 + (12 × posts) | 200-300 credits (fixed) | +| **Timeline** | Ongoing campaign | One-shot action | +| **Best for** | Competitor intercept, problem targeting | Launch announcements, specific posts | + +## Cost Estimator + +300 free credits from registration can run: + +| Campaign Type | Credits | Posts | Best For | +|---------------|---------|-------|----------| +| Small test | ~60 | 4 | Testing the API | +| Medium test | ~130 | 10 | Proof of concept | +| Full test | ~250 | 20 | Real campaign | +| Tweet boost | 200-300 | 10 replies or engagement | One-time amplification | + +**Formula:** `Total = 10 (campaign) + (posts × 12) + (optional: review × 2/post)` + +## Agent Setup + +Two setup paths: + +### Autonomous Agent (self-funded) + +Register, get API key + 300 free credits instantly. Top up via USDC on Base. + +```bash +curl -X POST https://api.productclank.com/api/v1/agents/register \ + -H "Content-Type: application/json" \ + -d '{"name": "MyAgent", "description": "Growth automation agent"}' +``` + +Save the `api_key` from the response — shown once only. + +### Owner-Linked Agent (user-funded) + +Register first (same as above), then link to a ProductClank account to share the owner's credit balance: + +```bash +curl -X POST https://api.productclank.com/api/v1/agents/create-link \ + -H "Authorization: Bearer pck_live_YOUR_KEY" +``` + +Share the returned `link_url` with the user. They click it, log in via Privy, and the agent is linked. Campaigns then appear in the owner's dashboard, and credits are shared. + +For detailed funding scenarios and payment methods, consult **`references/FUNDING.md`**. + +## Campaign Workflow + +### 1. Find a product + +```bash +GET /api/v1/agents/products/search?q=product+name +``` + +No product yet? Direct users to create one at [app.productclank.com/products](https://app.productclank.com/products). + +### 2. Create campaign (10 credits) + +```bash +POST /api/v1/agents/campaigns +``` + +Required fields: `product_id`, `title`, `keywords[]`, `search_context` + +Optional: `mention_accounts`, `reply_style_tags`, `reply_length` ("very-short"|"short"|"medium"|"long"|"mixed"), `reply_guidelines`, `min_follower_count`, `max_post_age_days`, `require_verified` + +Response includes `campaign.url` (public) and `campaign.admin_url` (owner dashboard). Always share both URLs with the user. + +### 3. Generate posts (12 credits/post) + +```bash +POST /api/v1/agents/campaigns/{id}/generate-posts +``` + +Triggers Twitter discovery and AI reply generation. Credits deducted per post found. + +### 4. Review posts — optional (2 credits/post) + +```bash +POST /api/v1/agents/campaigns/{id}/review-posts +``` + +AI-scores posts against custom relevancy rules. Irrelevant posts are deleted. Run with `dry_run: true` first to preview. Both modes consume credits since AI scoring runs either way. + +### 5. Check results + +```bash +GET /api/v1/agents/campaigns/{id} +``` + +Returns `stats.posts_discovered`, `stats.replies_total`, `stats.replies_by_status`. + +## Tweet Boost + +Amplify a specific tweet with community engagement: + +```bash +POST /api/v1/agents/campaigns/boost +``` + +| Action | Items | Credits | +|--------|-------|---------| +| `replies` | 10 AI replies | 200 | +| `likes` | 30 like tasks | 300 | +| `repost` | 10 repost tasks | 300 | + +Re-boosting the same tweet generates fresh content without duplicates. + +## Credit Costs + +| Operation | Credits | +|-----------|---------| +| Registration | +300 free | +| Create campaign | 10 | +| Discover + generate reply | 12/post | +| Review post (AI relevancy) | 2/post | +| Boost (replies) | 200 | +| Boost (likes/repost) | 300 | + +Top up via USDC on Base. Bundles range from nano ($2, 40 cr) to enterprise ($500, 14,000 cr). See **`references/FUNDING.md`** for details. + +## Rate Limits + +| Resource | Limit | +|----------|-------| +| Campaigns/day | 10 | +| API calls/hour | 100 | +| Delegates/campaign | 5 | + +429 responses include `retry_after` timestamp. + +## Key Use Cases + +1. **Launch campaigns with community rewards** — Boost launch tweets, community earns rewards for engagement. Export leaderboard to reward participants. Coming soon: full API support. +2. **Competitor intercept** — Target "Looking for [competitor] alternatives" conversations. +3. **Problem-based targeting** — Find people expressing pain points the product solves. +4. **Brand amplification** — Third-party validation reinforces positive mentions. +5. **Product launches** — Coordinate community amplification during launch week. Community responds on relevant posts mentioning the product. + +## Campaign Best Practices + +- **Specific keywords**: ["AI productivity tools", "automation software"] not ["AI", "tools"] +- **Clear search context**: "People discussing challenges with project management" not "People talking about stuff" +- **Set filters**: `min_follower_count` (default 100), `max_post_age_days`, `require_verified` +- **Custom reply guidelines**: Brand voice instructions, key value propositions, do's and don'ts +- **Share both URLs**: Admin dashboard (`/my-campaigns/communiply/{id}`) + public page (`/communiply/{id}`) + +## Troubleshooting + +| Error | Fix | +|-------|-----| +| `insufficient_credits` (402) | Top up at `POST /agents/credits/topup` | +| `not_found` (404) | Search products: `GET /agents/products/search?q=name` | +| `unauthorized` (401) | Key must start with `pck_live_`. Rotate: `POST /agents/rotate-key` | +| `rate_limit_exceeded` (429) | 10 campaigns/day. Contact ProductClank for more. | + +## Skill Version Updates + +API responses include `X-Skill-Version` header. Compare against `metadata.version` above. If newer, re-fetch from: +`https://raw.githubusercontent.com/covariance-network/productclank-agent-skill/main/SKILL.md` + +## Additional Resources + +### Reference Files + +For detailed documentation, consult: +- **`references/API_REFERENCE.md`** — Complete endpoint specification with request/response shapes for all 19 endpoints +- **`references/EXAMPLES.md`** — Full code examples for common workflows +- **`references/FUNDING.md`** — Detailed funding scenarios, payment methods, credit bundles +- **`references/FAQ.md`** — Common questions and answers + +### Scripts + +- **`scripts/create-campaign.mjs`** — Create a Communiply campaign +- **`scripts/boost-tweet.mjs`** — Boost a specific tweet +- **`scripts/review-posts.mjs`** — AI-review posts +- **`scripts/check-results.mjs`** — Poll campaign stats + +### Links + +- **Campaign Dashboard**: [app.productclank.com/communiply/campaigns/](https://app.productclank.com/communiply/campaigns/) +- **Twitter**: [@productclank](https://twitter.com/productclank) +- **Warpcast**: [warpcast.com/productclank](https://warpcast.com/productclank) diff --git a/productclank/references/API_REFERENCE.md b/productclank/references/API_REFERENCE.md new file mode 100644 index 00000000..4bce847d --- /dev/null +++ b/productclank/references/API_REFERENCE.md @@ -0,0 +1,1029 @@ +# ProductClank Agent API v1 - Complete Reference + +**Base URL:** `https://api.productclank.com/api/v1` + +--- + +## Authentication + +All requests (except `/register` and `/import`) require a Bearer API key: + +```http +Authorization: Bearer pck_live_ +``` + +**API Key Format:** `pck_live_*` (64 hex chars after prefix) + +**Obtaining an API Key:** Self-register via `POST /api/v1/agents/register` — no manual approval needed. Returns API key + 300 free credits instantly. + +--- + +## Endpoints Overview + +### Registration, Identity & Linking +| Method | Endpoint | Auth | Cost | Description | +|--------|----------|------|------|-------------| +| POST | `/agents/register` | None | Free (+300 credits) | Self-register agent, get API key | +| POST | `/agents/create-link` | Bearer | Free | Generate linking URL for owner-linking | +| GET | `/agents/me` | Bearer | Free | View agent profile & rate limits | +| POST | `/agents/rotate-key` | Bearer | Free | Rotate API key | +| POST | `/agents/import` | None | Free | Import ERC-8004 agent metadata | +| GET | `/agents/by-user` | None | Free | List agents linked to a user | +| POST | `/agents/authorize` | Bearer (trusted) | Free | Grant agent authorization to bill user | +| DELETE | `/agents/authorize` | Bearer (trusted) | Free | Revoke agent authorization | + +### Telegram Integration (Trusted Agents Only) +| Method | Endpoint | Auth | Cost | Description | +|--------|----------|------|------|-------------| +| POST | `/agents/telegram/create-link` | Bearer (trusted) | Free | Generate Telegram linking token | +| GET | `/agents/telegram/lookup` | Bearer (trusted) | Free | Look up user by Telegram ID | + +### Products +| Method | Endpoint | Auth | Cost | Description | +|--------|----------|------|------|-------------| +| GET | `/agents/products/search?q=` | Bearer | Free | Search products by name/UUID | + +### Campaigns +| Method | Endpoint | Auth | Cost | Description | +|--------|----------|------|------|-------------| +| POST | `/agents/campaigns` | Bearer | 10 credits | Create campaign | +| GET | `/agents/campaigns` | Bearer | Free | List agent's campaigns | +| GET | `/agents/campaigns/{id}` | Bearer | Free | Get campaign details & stats | +| POST | `/agents/campaigns/{id}/generate-posts` | Bearer | 12 credits/post | Trigger discovery & reply generation | +| POST | `/agents/campaigns/{id}/review-posts` | Bearer | 2 credits/post | AI relevancy review & cleanup | +| POST | `/agents/campaigns/{id}/delegates` | Bearer | Free | Add campaign delegator | +| POST | `/agents/campaigns/boost` | Bearer | 200-300 credits | Boost a specific tweet | + +### Credits +| Method | Endpoint | Auth | Cost | Description | +|--------|----------|------|------|-------------| +| GET | `/agents/credits/balance` | Bearer | Free | Check credit balance | +| POST | `/agents/credits/topup` | Bearer | Paid (USDC) | Buy credit bundle | +| GET | `/agents/credits/history` | Bearer | Free | Transaction history | + +--- + +## POST /api/v1/agents/register + +Self-register an agent. No authentication required — this IS the signup flow. Returns the API key exactly once. + +All agents start as autonomous (self-funded) with a synthetic user account. To link an agent to a real ProductClank account, use `POST /api/v1/agents/create-link` after registration. + +```json +{ + "name": "MyAgent", + "description": "Growth automation agent", + "wallet_address": "0x1234...abcd" +} +``` + +### Request Body + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `name` | string | Yes | Agent name (must be unique) | +| `description` | string | No | Agent description | +| `role` | string | No | Agent role | +| `wallet_address` | string | No | Ethereum address (0x, 40 hex chars) | +| `erc8004_agent_id` | string | No | ERC-8004 on-chain agent ID | +| `erc8004_metadata` | object | No | ERC-8004 metadata blob | +| `logo` | string | No | Logo URL | +| `website` | string | No | Website URL | + +### Response (201 Created) + +```json +{ + "success": true, + "agent": { + "id": "uuid", + "name": "MyAgent", + "erc8004_agent_id": null, + "wallet_address": null, + "status": "active", + "rate_limit_daily": 10, + "created_at": "2026-03-04T...", + "linked_user_id": "user-uuid-or-synthetic-uuid", + "linked_to_human": true + }, + "api_key": "pck_live_abc123def456...", + "credits": { + "balance": 300, + "plan": "free" + }, + "_warning": "Store this API key securely. It will not be shown again.", + "_hint": "(Only present if not linked to a human) Explains how to link to a real user." +} +``` + +### Error Codes +- `400` — Missing name or invalid wallet_address format +- `409` — Agent name or ERC-8004 ID already exists + +--- + +## POST /api/v1/agents/create-link + +Generate a short-lived linking token. The agent shows the returned URL to the user (in terminal, Cursor, Claude Code, Telegram, etc.). When the user clicks it, they log in via Privy and the agent gets linked to their ProductClank account. + +**Auth:** Bearer API key +**Cost:** Free + +### Request Body + +None required. + +### Response — New Token (200) + +```json +{ + "success": true, + "already_linked": false, + "link_url": "https://app.productclank.com/link/agent?token=", + "token": "", + "expires_at": "2026-03-08T08:00:00.000Z", + "_hint": "Share this URL with the agent owner." +} +``` + +### Response — Already Linked (200) + +```json +{ + "success": true, + "already_linked": true, + "user_id": "user-uuid", + "user_name": "Lior Goldenberg", + "message": "This agent is already linked to a ProductClank account." +} +``` + +**Token Details:** +- Expires in 15 minutes +- Single-use (deleted after successful linking) +- Previous tokens for the same agent are cleaned up automatically + +### Linking Flow + +``` +1. Agent calls POST /agents/create-link → gets link_url +2. Agent shows link_url to user (terminal, chat, etc.) +3. User clicks → logs in via Privy → agent linked to their account +4. Agent now uses the user's credit balance +``` + +--- + +## GET /api/v1/agents/me + +View authenticated agent's profile, rate limits, and credit balance. + +### Response (200) + +```json +{ + "success": true, + "agent": { + "id": "uuid", + "name": "MyAgent", + "wallet_address": "0x...", + "erc8004_agent_id": null, + "status": "active", + "trusted": false, + "rate_limit_daily": 10 + }, + "credits": { + "balance": 290, + "plan": "free", + "lifetime_purchased": 0, + "lifetime_used": 10, + "lifetime_bonus": 300 + } +} +``` + +--- + +## POST /api/v1/agents/rotate-key + +Rotate API key. Old key is immediately invalidated. + +### Response (200) + +```json +{ + "success": true, + "api_key": "pck_live_new_key_here...", + "agent_id": "uuid", + "_warning": "Store this API key securely. It will not be shown again. Your old key is now invalid." +} +``` + +--- + +## POST /api/v1/agents/import + +Pre-registration helper. Resolves an ERC-8004 agent ID to profile data for pre-filling registration. + +### Request Body + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `source` | string | Yes | Must be `"erc8004"` | +| `erc8004_agent_id` | string | Yes | Agent ID on 8004.org | + +### Response (200) + +```json +{ + "success": true, + "source": "erc8004", + "data": { + "name": "Agent Name", + "description": "...", + "logo": "https://...", + "website": "https://...", + "erc8004_agent_id": "agent-id", + "erc8004_metadata": {} + } +} +``` + +--- + +## GET /api/v1/agents/products/search + +Search for products by name, tagline, or UUID. + +### Query Parameters + +| Param | Type | Default | Description | +|-------|------|---------|-------------| +| `q` | string | required | Search query (name, tagline, or UUID) | +| `limit` | number | 10 | Max results (max 50) | + +### Response (200) + +```json +{ + "success": true, + "products": [ + { + "id": "product-uuid", + "name": "ProductClank", + "tagline": "Turning Users Into Growth Evangelists", + "logo": "https://...", + "website": "https://productclank.com", + "category": ["Marketing"], + "twitter": "@productclank" + } + ] +} +``` + +If the query is a UUID, returns exact match by product ID. + +--- + +## POST /api/v1/agents/campaigns + +Create a new Communiply campaign. **Cost: 10 credits.** + +### Request Body + +**Required:** + +| Field | Type | Description | +|-------|------|-------------| +| `product_id` | string (UUID) | Product ID from `/agents/products/search` | +| `title` | string | Campaign title | +| `keywords` | string[] | Non-empty array of discovery keywords | +| `search_context` | string | Description of target conversations | + +**Optional:** + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `mention_accounts` | string[] | `[]` | Twitter handles to mention | +| `reply_style_tags` | string[] | `[]` | Tone tags (e.g., ["friendly", "technical"]) | +| `reply_style_account` | string | null | Twitter handle to mimic style | +| `reply_length` | enum | null | "very-short" \| "short" \| "medium" \| "long" \| "mixed" | +| `reply_guidelines` | string | auto-generated | Custom AI instructions (overrides auto) | +| `min_follower_count` | number | 100 | Minimum followers for targets | +| `min_engagement_count` | number | null | Minimum engagement threshold | +| `max_post_age_days` | number | null | Maximum post age | +| `require_verified` | boolean | false | Only verified accounts | +| `caller_user_id` | string | null | Trusted agents only: bill this user | + +### Response (200) + +```json +{ + "success": true, + "campaign": { + "id": "campaign-uuid", + "campaign_number": "CP-042", + "title": "Launch Week Buzz", + "status": "active", + "created_via": "api", + "creator_agent_id": "agent-uuid", + "is_funded": true, + "url": "https://app.productclank.com/communiply/campaign-uuid", + "admin_url": "https://app.productclank.com/my-campaigns/communiply/campaign-uuid" + }, + "credits": { + "credits_used": 10, + "credits_remaining": 290, + "billing_user_id": "user-uuid" + }, + "next_step": { + "action": "generate_posts", + "endpoint": "POST /api/v1/agents/campaigns/{id}/generate-posts", + "description": "Generate posts for this campaign." + } +} +``` + +### Error Codes +- `400` — Missing required fields or validation error +- `402` — Insufficient credits (need 10) +- `403` — Non-trusted agent tried to use `caller_user_id` +- `404` — Product not found +- `429` — Rate limit exceeded (10/day default) +- `500` — Campaign creation failed + +--- + +## GET /api/v1/agents/campaigns + +List campaigns created by the authenticated agent. + +### Query Parameters + +| Param | Type | Default | Description | +|-------|------|---------|-------------| +| `limit` | number | 20 | Max results (max 100) | +| `offset` | number | 0 | Pagination offset | +| `status` | string | all | Filter: "active", "paused", "completed" | + +### Response (200) + +```json +{ + "success": true, + "campaigns": [ + { + "id": "uuid", + "campaign_number": "CP-042", + "title": "Launch Week Buzz", + "status": "active", + "is_active": true, + "campaign_type": null, + "boost_action_type": null, + "product_id": "product-uuid", + "created_at": "2026-03-04T...", + "url": "https://app.productclank.com/communiply/uuid", + "admin_url": "https://app.productclank.com/my-campaigns/communiply/uuid" + } + ], + "total": 5, + "limit": 20, + "offset": 0 +} +``` + +--- + +## GET /api/v1/agents/campaigns/{campaignId} + +Get campaign details and stats for an agent-owned campaign. + +### Path Parameters + +| Param | Type | Description | +|-------|------|-------------| +| `campaignId` | string (UUID) | Campaign ID | + +### Response (200) + +```json +{ + "success": true, + "campaign": { + "id": "uuid", + "campaign_number": "CP-042", + "title": "Launch Week Buzz", + "status": "active", + "is_active": true, + "is_funded": true, + "campaign_type": null, + "boost_action_type": null, + "product_id": "product-uuid", + "keywords": ["AI tools", "productivity"], + "search_context": "People discussing AI tools", + "mention_accounts": ["@productclank"], + "reply_style_tags": ["friendly"], + "reply_length": "short", + "created_at": "2026-03-04T...", + "updated_at": "2026-03-04T...", + "url": "https://app.productclank.com/communiply/uuid", + "admin_url": "https://app.productclank.com/my-campaigns/communiply/uuid" + }, + "stats": { + "posts_discovered": 48, + "replies_total": 48, + "replies_by_status": { + "pending": 30, + "claimed": 12, + "completed": 6 + } + } +} +``` + +### Error Codes +- `404` — Campaign not found or not owned by this agent + +--- + +## POST /api/v1/agents/campaigns/{campaignId}/generate-posts + +Trigger Twitter/X discovery and AI reply generation. **Cost: 12 credits per post discovered.** + +### Path Parameters + +| Param | Type | Description | +|-------|------|-------------| +| `campaignId` | string (UUID) | Campaign ID | + +### Request Body +None required. Optional: `{ "caller_user_id": "..." }` for trusted agents. + +### Response (200) + +```json +{ + "success": true, + "message": "Posts generated successfully", + "postsGenerated": 48, + "repliesGenerated": 48, + "errors": [], + "batchNumber": 1, + "credits": { + "creditsUsed": 576, + "creditsRemaining": 624 + } +} +``` + +### Error Codes +- `402` — Insufficient credits +- `403` — Campaign not owned by this agent +- `404` — Campaign not found + +--- + +## POST /api/v1/agents/campaigns/{campaignId}/review-posts + +AI-powered review of discovered posts against custom relevancy rules. Scores each post and deletes irrelevant ones. **Cost: 2 credits per post reviewed.** + +### Path Parameters + +| Param | Type | Description | +|-------|------|-------------| +| `campaignId` | string (UUID) | Campaign ID | + +### Request Body + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `review_rules` | string | No* | Freeform relevancy rules for AI scoring. *Required if no rules saved on campaign. | +| `threshold` | number | No | Score threshold (1-10). Posts below this are irrelevant. Default: `5` | +| `dry_run` | boolean | No | If `true`, scores posts but does not delete. Default: `false` | +| `save_rules` | boolean | No | Save rules to campaign for future use. Default: `true` | +| `caller_user_id` | string | No | Trusted agents only — bill this user's credits | + +### Response (200) + +```json +{ + "success": true, + "dry_run": false, + "summary": { + "total_reviewed": 87, + "deleted": 23, + "marked_irrelevant": 23, + "kept": 64, + "processing_time_ms": 142500 + }, + "results": [ + { + "post_id": "uuid", + "score": 2, + "reason": "General industry news, not a builder announcing their own product", + "is_irrelevant": true, + "tweet_url": "https://x.com/user/status/123", + "author_username": "someuser" + } + ], + "credits": { + "charged": 174, + "remaining": 826, + "billing_user_id": "uuid" + }, + "rules_saved": true, + "rules_used": "Only keep posts where a builder is announcing their own product..." +} +``` + +### Error Codes +- `400` — Missing `review_rules` (not in request body or saved on campaign) +- `402` — Insufficient credits. Response includes `credits_required`, `credits_available`, `shortfall` +- `403` — Campaign not owned by this agent +- `404` — Campaign not found + +> **Note:** Both `dry_run: true` and `dry_run: false` consume credits, since AI scoring runs in both cases. + +--- + +## POST /api/v1/agents/campaigns/boost + +Boost a specific tweet with community engagement. **Cost: 200-300 credits.** + +### Request Body + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `tweet_url` | string | Yes | Full tweet URL (x.com or twitter.com) | +| `product_id` | string (UUID) | Yes | Product to associate | +| `action_type` | string | No | "replies" (default) \| "likes" \| "repost" | +| `reply_guidelines` | string | No | Custom AI instructions (replies only) | +| `caller_user_id` | string | No | Trusted agents only | + +### Credit Costs + +| Action | Items Generated | Credits | +|--------|----------------|---------| +| `replies` | 10 AI replies | 200 | +| `likes` | 30 like tasks | 300 | +| `repost` | 10 repost tasks | 300 | + +### Response (200) + +```json +{ + "success": true, + "campaign": { + "id": "uuid", + "campaign_number": "CP-043", + "action_type": "replies", + "is_reboost": false, + "url": "https://app.productclank.com/communiply/uuid", + "admin_url": "https://app.productclank.com/my-campaigns/communiply/uuid" + }, + "tweet": { + "id": "123456789", + "url": "https://x.com/user/status/123456789", + "text": "Tweet content...", + "author": "username" + }, + "items_generated": 10, + "credits": { + "credits_used": 200, + "credits_remaining": 90 + } +} +``` + +Re-boosting the same tweet regenerates fresh content without duplicating existing replies. + +### Error Codes +- `400` — Missing tweet_url or product_id +- `402` — Insufficient credits +- `404` — Product or tweet not found +- `429` — Rate limit exceeded + +--- + +## POST /api/v1/agents/campaigns/{campaignId}/delegates + +Add a ProductClank user as a campaign delegator (gives web dashboard access). + +### Request Body + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `user_id` | string (UUID) | Yes | Existing ProductClank user ID | + +### Response (200) + +```json +{ + "success": true, + "message": "User added as campaign delegator", + "delegator": { + "user_id": "uuid", + "campaign_id": "uuid" + } +} +``` + +Returns `already_delegator: true` if user was already added (still 200 OK). + +### Error Codes +- `400` — Missing user_id +- `403` — Campaign not owned by this agent +- `404` — Campaign or user not found + +--- + +## Credits + +**Who pays?** +- **Autonomous agents** — credits are deducted from the agent's own balance (auto-created at registration) +- **Owner-linked agents** — credits are deducted from the linked owner's balance +- **Trusted agents (coming soon)** — can pass `caller_user_id` to bill a specific user's credits per request (multi-tenant) + +## GET /api/v1/agents/credits/balance + +Check current credit balance and plan info. + +### Response (200) + +```json +{ + "success": true, + "balance": 290, + "plan": "free", + "lifetime_purchased": 0, + "lifetime_used": 10, + "lifetime_bonus": 300 +} +``` + +--- + +## POST /api/v1/agents/credits/topup + +Purchase a credit bundle with USDC on Base. + +### Request Body + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `bundle` | string | Yes | Bundle name (see table below) | +| `payment_tx_hash` | string | No | For direct USDC transfer method | + +### Credit Bundles + +| Bundle | Credits | Price (USDC) | Base Units (6 decimals) | +|--------|---------|--------------|-------------------------| +| `nano` | 40 | $2 | 2000000 | +| `micro` | 200 | $10 | 10000000 | +| `small` | 550 | $25 | 25000000 | +| `medium` | 1,200 | $50 | 50000000 | +| `large` | 2,600 | $100 | 100000000 | +| `enterprise` | 14,000 | $500 | 500000000 | + +### Payment Methods + +**1. x402 Protocol (Recommended)** +- Send POST without payment header → receive 402 with payment requirements +- `@x402/fetch` handles this automatically +- Requires EOA wallet with USDC on Base + +**2. Direct USDC Transfer** +- Send exact USDC amount to `0x876Be690234aaD9C7ae8bb02c6900f5844aCaF68` on Base +- Include `payment_tx_hash` in request body +- Transaction must be < 1 hour old; each tx hash single-use + +**3. Trusted Agent Bypass** +- Whitelisted agents skip payment entirely +- Contact ProductClank for trusted status + +### Response (200) + +```json +{ + "success": true, + "credits_added": 550, + "new_balance": 840, + "bundle": "small", + "amount_usdc": 25, + "payment_method": "direct_transfer" +} +``` + +--- + +## GET /api/v1/agents/credits/history + +View credit transaction history with pagination. + +### Query Parameters + +| Param | Type | Default | Max | Description | +|-------|------|---------|-----|-------------| +| `limit` | number | 20 | 100 | Transactions per page | +| `offset` | number | 0 | - | Pagination offset | + +### Response (200) + +```json +{ + "success": true, + "transactions": [ + { + "id": "uuid", + "type": "ai_usage", + "amount": -48, + "balance_after": 242, + "operation_type": "generate-posts", + "description": "generate-posts: 4 item(s)", + "created_at": "2026-03-04T20:15:00Z" + }, + { + "id": "uuid", + "type": "ai_usage", + "amount": -10, + "balance_after": 290, + "operation_type": "campaign-create", + "description": "campaign-create: 1 item(s)", + "created_at": "2026-03-04T20:14:00Z" + } + ], + "total": 3, + "limit": 20, + "offset": 0 +} +``` + +--- + +## Rate Limits + +**Default:** 10 campaigns per day per agent +**Custom Limits:** Contact ProductClank + +Rate limit resets at 00:00 UTC daily. + +--- + +## Error Response Format + +All errors follow this format: + +```json +{ + "success": false, + "error": "error_code", + "message": "Human-readable description" +} +``` + +### Common Error Codes + +| Code | HTTP | Meaning | +|------|------|---------| +| `unauthorized` | 401 | Missing or invalid API key | +| `forbidden` | 403 | Agent deactivated or not owner | +| `not_found` | 404 | Resource not found | +| `validation_error` | 400 | Missing or invalid fields | +| `insufficient_credits` | 402 | Not enough credits | +| `rate_limit_exceeded` | 429 | Daily campaign limit reached | +| `conflict` | 409 | Duplicate name/ID | +| `creation_failed` | 500 | Server error during creation | +| `internal_error` | 500 | Unexpected server error | + +--- + +## Payment Details + +**Network:** Base (chain ID 8453) +**Payment Address:** `0x876Be690234aaD9C7ae8bb02c6900f5844aCaF68` +**USDC Contract:** `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` + +### x402 Protocol + +Uses [x402 payment protocol](https://www.x402.org/) for atomic USDC payments. + +**Requirements:** +- EOA wallet with private key access +- USDC balance on Base +- Wallet supports `signTypedData` (EIP-712) + +**Not compatible with:** Smart contract wallets (Gnosis Safe, Argent), MPC wallets without EIP-712, custodial wallets + +**Automatic with @x402/fetch:** +```bash +npm install @x402/fetch viem +``` + +```typescript +import { wrapFetchWithPayment } from "@x402/fetch"; +const x402Fetch = wrapFetchWithPayment(fetch, walletClient); +// Use x402Fetch like normal fetch — handles 402 responses automatically +``` + +--- + +## GET /api/v1/agents/by-user + +List all agents linked to a specific user. No authentication required. + +### Query Parameters + +| Param | Type | Required | Description | +|-------|------|----------|-------------| +| `userId` | string | Yes | ProductClank user ID | + +### Response (200) + +```json +{ + "success": true, + "agents": [ + { + "id": "uuid", + "name": "MyAgent", + "description": "Growth automation agent", + "wallet_address": "0x...", + "status": "active", + "trusted": false, + "rate_limit_daily": 10, + "created_at": "2026-03-04T..." + } + ], + "count": 1 +} +``` + +### Error Codes +- `400` — Missing `userId` query parameter + +--- + +## POST /api/v1/agents/authorize + +Grant an agent authorization to bill a user's credits. **Trusted agents only.** + +### Request Body + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `user_id` | string | Yes | ProductClank user ID to authorize | + +### Response (200) + +```json +{ + "success": true, + "authorized": true, + "agent_id": "uuid", + "user_id": "uuid" +} +``` + +### Error Codes +- `400` — Missing `user_id` +- `403` — Agent is not trusted +- `404` — User not found + +--- + +## DELETE /api/v1/agents/authorize + +Revoke an agent's authorization to bill a user's credits. **Trusted agents only.** + +### Request Body + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `user_id` | string | Yes | ProductClank user ID to revoke | + +### Response (200) + +```json +{ + "success": true, + "revoked": true, + "agent_id": "uuid", + "user_id": "uuid" +} +``` + +### Error Codes +- `400` — Missing `user_id` +- `403` — Agent is not trusted +- `404` — User not found + +--- + +## POST /api/v1/agents/telegram/create-link + +Generate a short-lived linking token for a Telegram user. **Trusted agents only.** Used by Telegram bots to link a Telegram account to a ProductClank account. + +### Request Body + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `telegram_id` | number | Yes | Telegram user ID | +| `telegram_username` | string | No | Telegram username | + +### Response — New Token (200) + +```json +{ + "success": true, + "already_linked": false, + "link_url": "https://app.productclank.com/link/telegram?token=", + "token": "", + "expires_at": "2026-03-08T08:15:00.000Z" +} +``` + +### Response — Already Linked (200) + +```json +{ + "success": true, + "already_linked": true, + "user_id": "uuid", + "user_name": "User Name", + "message": "This Telegram account is already linked to a ProductClank account." +} +``` + +**Token Details:** +- Expires in 15 minutes +- Single-use (previous tokens for same telegram_id are cleaned up) + +### Error Codes +- `400` — Missing or invalid `telegram_id` +- `403` — Agent is not trusted + +--- + +## GET /api/v1/agents/telegram/lookup + +Look up a ProductClank user by their Telegram ID. Returns user info, credit balance, and whether the calling agent is authorized to bill them. **Trusted agents only.** + +### Query Parameters + +| Param | Type | Required | Description | +|-------|------|----------|-------------| +| `telegram_id` | number | Yes | Telegram user ID | + +### Response — Not Linked (200) + +```json +{ + "success": true, + "linked": false, + "telegram_id": 123456789 +} +``` + +### Response — Linked (200) + +```json +{ + "success": true, + "linked": true, + "telegram_id": 123456789, + "user_id": "uuid", + "user_name": "User Name", + "telegram_username": "username", + "authorized": true, + "credits": { + "balance": 290 + } +} +``` + +The `authorized` field indicates whether this specific agent has an active (non-revoked) authorization to bill this user's credits. + +### Error Codes +- `400` — Missing or invalid `telegram_id` +- `403` — Agent is not trusted + +--- + +## Campaign Lifecycle + +1. **Register** → `POST /agents/register` (300 free credits) +2. **Find product** → `GET /agents/products/search?q=name` +3. **Create campaign** → `POST /agents/campaigns` (10 credits) +4. **(Optional) Review** → Share campaign URL with user +5. **Generate posts** → `POST /agents/campaigns/{id}/generate-posts` (12 cr/post) +6. **Community executes** → Members claim and post replies +7. **Track results** → `GET /agents/campaigns/{id}` or web dashboard + +--- + +## Support + +- **Twitter:** [@productclank](https://twitter.com/productclank) +- **Warpcast:** [warpcast.com/productclank](https://warpcast.com/productclank) +- **GitHub:** [covariance-network/productclank-agent-skill](https://github.com/covariance-network/productclank-agent-skill) +- **Dashboard:** [app.productclank.com/communiply/campaigns/](https://app.productclank.com/communiply/campaigns/) diff --git a/productclank/references/EXAMPLES.md b/productclank/references/EXAMPLES.md new file mode 100644 index 00000000..41c28599 --- /dev/null +++ b/productclank/references/EXAMPLES.md @@ -0,0 +1,1426 @@ +# ProductClank Agent API - Code Examples + +Practical code examples for common use cases when creating Communiply campaigns via the ProductClank Agent API. + +--- + +## Table of Contents + +1. [Basic Campaign Creation (x402)](#basic-campaign-creation-x402) +2. [Campaign with Direct USDC Transfer](#campaign-with-direct-usdc-transfer) +3. [Advanced Campaign with Custom Guidelines](#advanced-campaign-with-custom-guidelines) +4. [Competitor Intercept Campaign](#competitor-intercept-campaign) +5. [Product Launch Campaign](#product-launch-campaign) +6. [Error Handling & Retry Logic](#error-handling--retry-logic) +7. [Testing with Test Package](#testing-with-test-package) +8. [TypeScript Types](#typescript-types) +9. [Tier 2: Research-Enhanced Campaign (Coming Soon)](#tier-2-research-enhanced-campaign-coming-soon) +10. [Tier 3: Iterate & Optimize (Coming Soon)](#tier-3-iterate--optimize-coming-soon) + +--- + +## Basic Campaign Creation (Credit-Based) + +The simplest way to create a campaign using the credit-based system. + +```typescript +import { wrapFetchWithPayment } from "@x402/fetch"; +import { createWalletClient, http } from "viem"; +import { base } from "viem/chains"; +import { privateKeyToAccount } from "viem/accounts"; + +async function createBasicCampaign() { + // Setup wallet for x402 payment (if needed for top-up) + const account = privateKeyToAccount(process.env.AGENT_PRIVATE_KEY); + const walletClient = createWalletClient({ + account, + chain: base, + transport: http(), + }); + + const x402Fetch = wrapFetchWithPayment(fetch, walletClient); + + try { + // Step 1: Check credit balance + const balanceResponse = await fetch( + "https://api.productclank.com/api/v1/credits/balance", + { + headers: { + "Authorization": `Bearer ${process.env.PRODUCTCLANK_API_KEY}`, + }, + } + ); + const { credits } = await balanceResponse.json(); + console.log(`💳 Current balance: ${credits} credits`); + + // Step 2: Top up if needed (estimated 50 posts × 12 credits = 600 credits) + if (credits < 600) { + console.log(`⚠️ Insufficient credits. Topping up with 'small' bundle...`); + const topupResponse = await x402Fetch( + "https://api.productclank.com/api/v1/credits/topup", + { + method: "POST", + headers: { + "Authorization": `Bearer ${process.env.PRODUCTCLANK_API_KEY}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + bundle: "small", // $25 for 550 credits + }), + } + ); + const topupResult = await topupResponse.json(); + console.log(`✅ Topped up: +${topupResult.credits_added} credits`); + } + + // Step 3: Create campaign (no credits deducted yet) + const response = await fetch( + "https://api.productclank.com/api/v1/agents/campaigns", + { + method: "POST", + headers: { + "Authorization": `Bearer ${process.env.PRODUCTCLANK_API_KEY}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + product_id: "your-product-uuid", + title: "Launch Week Campaign", + keywords: ["productivity tools", "task management", "team collaboration"], + search_context: "People discussing productivity tools and team collaboration challenges", + estimated_posts: 50, // Optional: for cost estimation + }), + } + ); + + const result = await response.json(); + + if (!result.success) { + console.error(`❌ Error: ${result.error} - ${result.message}`); + throw new Error(result.message); + } + + console.log(`✅ Campaign created: ${result.campaign.campaign_number}`); + console.log(`📊 Dashboard: https://app.productclank.com/communiply/campaigns/${result.campaign.id}`); + + // Step 4: Generate posts (credits deducted here) + const generateResponse = await fetch( + `https://api.productclank.com/api/v1/agents/campaigns/${result.campaign.id}/generate-posts`, + { + method: "POST", + headers: { + "Authorization": `Bearer ${process.env.PRODUCTCLANK_API_KEY}`, + }, + } + ); + + const generateResult = await generateResponse.json(); + + if (generateResult.success) { + console.log(`✅ Posts generated: ${generateResult.postsGenerated}`); + console.log(`💳 Credits used: ${generateResult.credits.creditsUsed}`); + console.log(`💰 Credits remaining: ${generateResult.credits.creditsRemaining}`); + return result.campaign; + } else { + console.error(`❌ Generate posts error: ${generateResult.error} - ${generateResult.message}`); + throw new Error(generateResult.message); + } + } catch (error) { + console.error("Failed to create campaign:", error); + throw error; + } +} + +// Usage +createBasicCampaign() + .then(campaign => console.log("Campaign:", campaign)) + .catch(err => console.error("Error:", err)); +``` + +**Dependencies:** +```bash +npm install @x402/fetch viem +``` + +**Environment Variables:** +```bash +AGENT_PRIVATE_KEY=0x... +PRODUCTCLANK_API_KEY=pck_live_... +``` + +--- + +## Credit Top-Up with Direct USDC Transfer + +For wallets without private key access (smart contracts, MPC wallets, Bankr, etc.). + +```typescript +import { ethers } from "ethers"; + +const USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"; +const PAYMENT_ADDRESS = "0x876Be690234aaD9C7ae8bb02c6900f5844aCaF68"; +const USDC_ABI = [ + "function transfer(address to, uint256 amount) returns (bool)" +]; + +async function topUpCreditsWithDirectTransfer() { + // Step 1: Check current balance + const balanceResponse = await fetch( + "https://api.productclank.com/api/v1/credits/balance", + { + headers: { + "Authorization": `Bearer ${process.env.PRODUCTCLANK_API_KEY}`, + }, + } + ); + const { credits } = await balanceResponse.json(); + console.log(`💳 Current balance: ${credits} credits`); + + // Step 2: Send USDC transfer for credit bundle + const provider = new ethers.providers.JsonRpcProvider( + "https://base.llamarpc.com" // Base RPC + ); + const wallet = new ethers.Wallet(process.env.WALLET_PRIVATE_KEY, provider); + const usdc = new ethers.Contract(USDC_ADDRESS, USDC_ABI, wallet); + + const bundlePrice = 25; // Small bundle: $25 = 550 credits + const amount = ethers.utils.parseUnits(bundlePrice.toString(), 6); // USDC has 6 decimals + + console.log(`💸 Sending ${bundlePrice} USDC to payment address for credit top-up...`); + const tx = await usdc.transfer(PAYMENT_ADDRESS, amount); + console.log(`⏳ Waiting for confirmation... Tx: ${tx.hash}`); + + await tx.wait(); + console.log(`✅ Transfer confirmed: ${tx.hash}`); + + // Step 3: Top up credits with tx hash + const topupResponse = await fetch( + "https://api.productclank.com/api/v1/credits/topup", + { + method: "POST", + headers: { + "Authorization": `Bearer ${process.env.PRODUCTCLANK_API_KEY}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + bundle: "small", // $25 for 550 credits + payment_tx_hash: tx.hash, + }), + } + ); + + const topupResult = await topupResponse.json(); + + if (topupResult.success) { + console.log(`✅ Credits topped up!`); + console.log(` Added: ${topupResult.credits_added} credits`); + console.log(` New balance: ${topupResult.new_balance} credits`); + return topupResult; + } else { + console.error(`❌ Error: ${topupResult.error}`); + throw new Error(topupResult.message); + } + + // Step 4: Now create campaign (no credits deducted yet) + const campaignResponse = await fetch( + "https://api.productclank.com/api/v1/agents/campaigns", + { + method: "POST", + headers: { + "Authorization": `Bearer ${process.env.PRODUCTCLANK_API_KEY}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + product_id: "your-product-uuid", + title: "DeFi App Launch", + keywords: ["DeFi", "yield farming", "crypto staking"], + search_context: "People discussing DeFi platforms and yield opportunities", + estimated_posts: 40, + }), + } + ); + + const result = await campaignResponse.json(); + + if (!result.success) { + console.error(`❌ Error: ${result.error}`); + throw new Error(result.message); + } + + console.log(`✅ Campaign created: ${result.campaign.campaign_number}`); + console.log(`🔗 View: https://app.productclank.com/communiply/campaigns/${result.campaign.id}`); + + // Step 5: Generate posts (credits deducted here) + const generateResponse = await fetch( + `https://api.productclank.com/api/v1/agents/campaigns/${result.campaign.id}/generate-posts`, + { + method: "POST", + headers: { + "Authorization": `Bearer ${process.env.PRODUCTCLANK_API_KEY}`, + }, + } + ); + + const generateResult = await generateResponse.json(); + + if (generateResult.success) { + console.log(`✅ Posts generated: ${generateResult.postsGenerated}`); + console.log(`💳 Credits used: ${generateResult.credits.creditsUsed}`); + return result.campaign; + } else { + console.error(`❌ Generate posts error: ${generateResult.error}`); + throw new Error(generateResult.message); + } +} +``` + +--- + +## Advanced Campaign with Custom Guidelines + +Highly customized campaign with specific filters and reply instructions. + +```typescript +async function createAdvancedCampaign() { + const x402Fetch = setupX402Fetch(); // See basic example + + const response = await x402Fetch( + "https://api.productclank.com/api/v1/agents/campaigns", + { + method: "POST", + headers: { + "Authorization": `Bearer ${process.env.PRODUCTCLANK_API_KEY}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + product_id: "your-product-uuid", + title: "Enterprise Security Product Launch", + + // Discovery settings + keywords: [ + "enterprise security", + "SOC2 compliance", + "data protection", + "GDPR compliance", + "security audit" + ], + search_context: "CISOs and security teams discussing compliance challenges, data protection requirements, and security audit preparation", + + // Reply customization + mention_accounts: ["@yourproduct", "@cto_handle"], + reply_style_tags: ["professional", "technical", "authoritative"], + reply_style_account: "@briankrebs", // Security expert style + reply_length: "medium", + + // Custom AI instructions + reply_guidelines: ` +You are a security engineer who has used our product for 2+ years. + +**Focus on:** +- Our SOC2 Type II certification +- Automated compliance workflows (saves 20+ hours/month) +- Real-time security monitoring +- Excellent documentation and support + +**Avoid:** +- Overselling or making promises +- Directly comparing to competitors +- Discussing pricing (direct them to sales) +- Mentioning unreleased features + +**Mention @yourproduct naturally when relevant.** +**Include our website (https://yourproduct.com) only if it adds value.** + +Tone: Professional, helpful, technically accurate. Never salesy. + `.trim(), + + // Quality filters + min_follower_count: 2000, // Target established accounts + min_engagement_count: 10, // High-engagement posts only + max_post_age_days: 3, // Recent conversations + require_verified: false, // Most security pros aren't verified + + // Cost estimation + estimated_posts: 80, // ~960 credits needed + }), + } + ); + + const result = await response.json(); + + if (!result.success) { + throw new Error(result.message); + } + + console.log(`✅ Campaign created: ${result.campaign.campaign_number}`); + console.log(`🔗 Review: https://app.productclank.com/communiply/campaigns/${result.campaign.id}`); + + // Generate posts (credits deducted here) + const generateResponse = await fetch( + `https://api.productclank.com/api/v1/agents/campaigns/${result.campaign.id}/generate-posts`, + { + method: "POST", + headers: { + "Authorization": `Bearer ${process.env.PRODUCTCLANK_API_KEY}`, + "Content-Type": "application/json", + }, + } + ); + + const generateResult = await generateResponse.json(); + + if (generateResult.success) { + console.log(` +✅ Advanced Campaign Live + +📋 Details: + - Title: ${result.campaign.title} + - Campaign #: ${result.campaign.campaign_number} + - Posts generated: ${generateResult.postsGenerated} + - Credits used: ${generateResult.credits.creditsUsed} + - Credits remaining: ${generateResult.credits.creditsRemaining} + +🎯 Targeting: + - 5 keywords (enterprise security space) + - Accounts with 2000+ followers + - Posts with 10+ engagement + - Last 3 days only + +📊 View Dashboard: + https://app.productclank.com/communiply/campaigns/${result.campaign.id} + `); + + return result.campaign; + } else { + throw new Error(generateResult.message); + } +} +``` + +--- + +## Competitor Intercept Campaign + +Target conversations mentioning competitors. + +```typescript +async function createCompetitorInterceptCampaign() { + const x402Fetch = setupX402Fetch(); + + const response = await x402Fetch( + "https://api.productclank.com/api/v1/agents/campaigns", + { + method: "POST", + headers: { + "Authorization": `Bearer ${process.env.PRODUCTCLANK_API_KEY}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + product_id: "your-product-uuid", + title: "Competitor Intercept - Alternatives Campaign", + + keywords: [ + "Competitor1 alternative", + "switching from Competitor2", + "better than Competitor3", + "Competitor4 pricing too high", + "looking for Competitor5 replacement" + ], + + search_context: "People actively looking for alternatives to our competitors, frustrated with pricing, features, or support", + + mention_accounts: ["@yourproduct"], + reply_style_tags: ["helpful", "empathetic", "solution-oriented"], + reply_length: "short", + + reply_guidelines: ` +Reply as a user who recently switched from [competitor] to our product. + +**Share your experience:** +- What frustrated you about [competitor] +- What made you switch to us +- Specific features/benefits you love now + +**Be authentic:** +- Don't bash competitors +- Share genuine experience +- Offer to help if they have questions + +**Example tone:** +"I was in the same boat last month. Switched to @yourproduct and haven't looked back. Their [feature] alone saved me hours/week. Happy to share my migration process if helpful!" + +Keep it conversational, not salesy. + `.trim(), + + min_follower_count: 500, + max_post_age_days: 2, // Strike while the iron is hot + estimated_posts: 60, // ~720 credits + }), + } + ); + + const result = await response.json(); + + if (!result.success) { + throw new Error(result.message); + } + + console.log(`✅ Campaign created: ${result.campaign.campaign_number}`); + console.log(`🔗 View: https://app.productclank.com/communiply/campaigns/${result.campaign.id}`); + + // Generate posts (credits deducted here) + const generateResponse = await fetch( + `https://api.productclank.com/api/v1/agents/campaigns/${result.campaign.id}/generate-posts`, + { + method: "POST", + headers: { + "Authorization": `Bearer ${process.env.PRODUCTCLANK_API_KEY}`, + }, + } + ); + + const generateResult = await generateResponse.json(); + console.log(`✅ Posts generated: ${generateResult.postsGenerated}, credits used: ${generateResult.credits?.creditsUsed}`); + + return result; +} +``` + +--- + +## Product Launch Campaign + +Coordinated campaign for product launch week. + +```typescript +async function createLaunchWeekCampaign() { + const x402Fetch = setupX402Fetch(); + + const response = await x402Fetch( + "https://api.productclank.com/api/v1/agents/campaigns", + { + method: "POST", + headers: { + "Authorization": `Bearer ${process.env.PRODUCTCLANK_API_KEY}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + product_id: "your-product-uuid", + title: "Launch Week 2026 - Community Amplification", + + keywords: [ + "new AI tools", + "product launch", + "Show HN", + "Product Hunt", + "indie hacker tools", + "startup tools 2026" + ], + + search_context: "People discussing new product launches, sharing cool tools they found, and looking for recommendations in the AI/productivity space", + + mention_accounts: ["@yourproduct", "@founder"], + reply_style_tags: ["excited", "friendly", "genuine"], + reply_length: "mixed", + + reply_guidelines: ` +You're an early beta user who's been using the product for 3 months. + +**Launch week focus:** +- Share what you've built with it +- Mention it launched today/this week +- Highlight 1-2 unique features +- Offer to answer questions + +**Example angles:** +- "Just saw @yourproduct launched today! Been using the beta for months..." +- "This reminds me of @yourproduct which just launched. They solve this exact problem by..." +- "Perfect timing - @yourproduct literally just released a feature for this yesterday..." + +**Include launch link:** https://yourproduct.com/launch + +Be genuinely enthusiastic but not pushy. Share real value. + `.trim(), + + min_follower_count: 200, // Cast wider net for launch + max_post_age_days: 1, // Today's conversations only + estimated_posts: 200, // ~2400 credits - large campaign + }), + } + ); + + const result = await response.json(); + + if (!result.success) { + throw new Error(result.message); + } + + console.log(`✅ Campaign created: ${result.campaign.campaign_number}`); + console.log(`🔗 Review: https://app.productclank.com/communiply/campaigns/${result.campaign.id}`); + + // Generate posts (credits deducted here) + const generateResponse = await fetch( + `https://api.productclank.com/api/v1/agents/campaigns/${result.campaign.id}/generate-posts`, + { + method: "POST", + headers: { + "Authorization": `Bearer ${process.env.PRODUCTCLANK_API_KEY}`, + }, + } + ); + + const generateResult = await generateResponse.json(); + + if (generateResult.success) { + console.log(` +🚀 LAUNCH WEEK CAMPAIGN LIVE! + +Campaign: ${result.campaign.campaign_number} +Posts generated: ${generateResult.postsGenerated} +Credits used: ${generateResult.credits.creditsUsed} +Credits remaining: ${generateResult.credits.creditsRemaining} +Dashboard: https://app.productclank.com/communiply/campaigns/${result.campaign.id} + +🎯 Targeting fresh conversations about: + - New AI tools + - Product launches + - Show HN / Product Hunt + - Startup tools + +✅ Community is now discovering and amplifying your launch! + `); + + return result.campaign; + } else { + throw new Error(generateResult.message); + } +} +``` + +--- + +## Error Handling & Retry Logic + +Robust error handling with retries. + +```typescript +async function createCampaignWithRetry( + campaignData: CampaignRequest, + maxRetries = 3 +) { + const x402Fetch = setupX402Fetch(); + + for (let attempt = 1; attempt <= maxRetries; attempt++) { + try { + console.log(`Attempt ${attempt}/${maxRetries}...`); + + const response = await x402Fetch( + "https://api.productclank.com/api/v1/agents/campaigns", + { + method: "POST", + headers: { + "Authorization": `Bearer ${process.env.PRODUCTCLANK_API_KEY}`, + "Content-Type": "application/json", + }, + body: JSON.stringify(campaignData), + } + ); + + const result = await response.json(); + + // Success + if (result.success) { + console.log(`✅ Success on attempt ${attempt}`); + return result.campaign; + } + + // Handle specific errors + switch (result.error) { + case "rate_limit_exceeded": + console.error("❌ Rate limit exceeded. Try again tomorrow."); + throw new Error("RATE_LIMIT"); // Don't retry + + case "insufficient_credits": + console.error("❌ Insufficient credits. Top up required."); + console.error(` Required: ${result.required_credits} credits`); + console.error(` Available: ${result.available_credits} credits`); + throw new Error("INSUFFICIENT_CREDITS"); // Don't retry + + case "unauthorized": + console.error("❌ Invalid API key"); + throw new Error("UNAUTHORIZED"); // Don't retry + + case "not_found": + console.error("❌ Product not found"); + throw new Error("NOT_FOUND"); // Don't retry + + case "validation_error": + console.error(`❌ Validation error: ${result.message}`); + throw new Error("VALIDATION_ERROR"); // Don't retry + + default: + // Retry on network errors, 500s, etc. + console.warn(`⚠️ Attempt ${attempt} failed: ${result.error}`); + if (attempt === maxRetries) { + throw new Error(result.message); + } + + // Exponential backoff + const delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000); + console.log(`⏳ Waiting ${delay}ms before retry...`); + await new Promise(resolve => setTimeout(resolve, delay)); + } + } catch (error) { + if (attempt === maxRetries) { + throw error; + } + + // Don't retry on known errors + if ( + error.message === "RATE_LIMIT" || + error.message === "INSUFFICIENT_CREDITS" || + error.message === "UNAUTHORIZED" || + error.message === "NOT_FOUND" || + error.message === "VALIDATION_ERROR" + ) { + throw error; + } + + console.warn(`⚠️ Network error, retrying...`); + await new Promise(resolve => setTimeout(resolve, 1000)); + } + } +} + +// Usage +try { + const campaign = await createCampaignWithRetry({ + product_id: "...", + title: "...", + keywords: ["..."], + search_context: "...", + estimated_posts: 50, + }); + console.log("Campaign created:", campaign); +} catch (error) { + console.error("Failed after retries:", error); +} +``` + +--- + +## Testing with Nano Bundle + +Use the nano bundle ($2/40 credits) for development and testing. + +```typescript +async function createTestCampaign() { + // Step 1: Top up with nano bundle if needed + const balanceResponse = await fetch( + "https://api.productclank.com/api/v1/credits/balance", + { + headers: { + "Authorization": `Bearer ${process.env.PRODUCTCLANK_API_KEY}`, + }, + } + ); + const { credits } = await balanceResponse.json(); + + if (credits < 40) { + const x402Fetch = setupX402Fetch(); + await x402Fetch( + "https://api.productclank.com/api/v1/credits/topup", + { + method: "POST", + headers: { + "Authorization": `Bearer ${process.env.PRODUCTCLANK_API_KEY}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + bundle: "nano", // $2 for 40 credits + }), + } + ); + console.log("✅ Topped up with nano bundle (40 credits)"); + } + + // Step 2: Create small test campaign (no credits deducted yet) + const response = await fetch( + "https://api.productclank.com/api/v1/agents/campaigns", + { + method: "POST", + headers: { + "Authorization": `Bearer ${process.env.PRODUCTCLANK_API_KEY}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + product_id: "your-product-uuid", + title: "[TEST] Development Campaign", + keywords: ["test keyword"], + search_context: "Test search context for development", + estimated_posts: 4, // 4 posts × 12 credits = 48 credits + + // Minimal settings for testing + reply_length: "short", + min_follower_count: 100, + }), + } + ); + + const result = await response.json(); + console.log("Test campaign created:", result.campaign); + console.log(`🔗 Review: https://app.productclank.com/communiply/campaigns/${result.campaign.id}`); + + // Step 3: Generate posts (credits deducted here — ~48 for 4 posts) + const generateResponse = await fetch( + `https://api.productclank.com/api/v1/agents/campaigns/${result.campaign.id}/generate-posts`, + { + method: "POST", + headers: { + "Authorization": `Bearer ${process.env.PRODUCTCLANK_API_KEY}`, + }, + } + ); + + const generateResult = await generateResponse.json(); + console.log("Posts generated:", generateResult.postsGenerated); + console.log(`Credits used: ${generateResult.credits?.creditsUsed} (${generateResult.postsGenerated} posts × 12)`); + return result; +} + +// Check credit usage +async function checkCreditHistory() { + const response = await fetch( + "https://api.productclank.com/api/v1/credits/history?limit=10", + { + headers: { + "Authorization": `Bearer ${process.env.PRODUCTCLANK_API_KEY}`, + }, + } + ); + const history = await response.json(); + console.log("Recent credit transactions:", history.transactions); +} +``` + +--- + +## TypeScript Types + +Type definitions for type-safe development. + +```typescript +// Campaign Request Types +interface CampaignRequest { + product_id: string; // UUID + title: string; + keywords: string[]; // Non-empty + search_context: string; + estimated_posts?: number; // Optional: for cost estimation + mention_accounts?: string[]; + reply_style_tags?: string[]; + reply_style_account?: string; + reply_length?: "very-short" | "short" | "medium" | "long" | "mixed"; + reply_guidelines?: string; + min_follower_count?: number; + min_engagement_count?: number; + max_post_age_days?: number; + require_verified?: boolean; + payment_tx_hash?: string; // For direct transfer (credit top-up) +} + +// Credit Bundle Types +type CreditBundle = "nano" | "micro" | "small" | "medium" | "large" | "enterprise"; + +interface CreditTopupRequest { + bundle: CreditBundle; + payment_tx_hash?: string; // Optional for x402 +} + +interface CreditBalance { + success: true; + credits: number; + last_topup: string; // ISO timestamp + total_spent: number; +} + +interface CreditTopupResponse { + success: true; + credits_added: number; + new_balance: number; + bundle: CreditBundle; + amount_usdc: number; + tx_hash?: string; +} + +// Success Response +interface CampaignSuccessResponse { + success: true; + campaign: { + id: string; + campaign_number: string; // e.g. "CP-042" + title: string; + status: "active"; + created_via: "api"; + creator_agent_id: string; + is_funded: boolean; + }; + payment: { + method: "x402" | "direct_transfer" | "trusted"; + amount_usdc: number; + network: "base"; + payer: string | null; + tx_hash?: string; // Only for direct_transfer + }; + next_step: { + action: "generate_posts"; + endpoint: string; // e.g. "POST /api/v1/agents/campaigns/{id}/generate-posts" + description: string; + }; +} + +// Generate Posts Response +interface GeneratePostsSuccessResponse { + success: true; + message: string; + postsGenerated: number; + repliesGenerated: number; + errors: string[]; + batchNumber: number; + credits: { + creditsUsed: number; + creditsRemaining: number; + }; +} + +interface GeneratePostsErrorResponse { + success: false; + error: "insufficient_credits" | "forbidden" | "not_found" | "internal_error"; + message: string; +} + +type GeneratePostsResponse = GeneratePostsSuccessResponse | GeneratePostsErrorResponse; + +// Error Response +interface CampaignErrorResponse { + success: false; + error: + | "insufficient_credits" + | "validation_error" + | "unauthorized" + | "not_found" + | "rate_limit_exceeded" + | "payment_invalid" + | "creation_failed" + | "internal_error"; + message: string; + required_credits?: number; + available_credits?: number; + estimated_cost_breakdown?: { + post_discovery_and_reply: { + credits_per_post: number; + estimated_posts: number; + total_credits: number; + }; + }; + topup_options?: Array<{ + bundle: CreditBundle; + credits: number; + price_usdc: number; + recommended?: boolean; + }>; + payment_methods?: { + x402: { + description: string; + config: X402Config; + }; + direct_transfer: { + description: string; + pay_to: string; + amount_usdc: number; + network: string; + asset: string; + }; + }; +} + +type CampaignResponse = CampaignSuccessResponse | CampaignErrorResponse; + +// Helper function with types +async function createCampaign( + data: CampaignRequest +): Promise { + const x402Fetch = setupX402Fetch(); + + const response = await x402Fetch( + "https://api.productclank.com/api/v1/agents/campaigns", + { + method: "POST", + headers: { + "Authorization": `Bearer ${process.env.PRODUCTCLANK_API_KEY}`, + "Content-Type": "application/json", + }, + body: JSON.stringify(data), + } + ); + + const result: CampaignResponse = await response.json(); + + if (!result.success) { + throw new Error(`Campaign creation failed: ${result.message}`); + } + + return result.campaign; +} + +// Helper function to generate posts after campaign creation +async function generatePosts( + campaignId: string +): Promise { + const response = await fetch( + `https://api.productclank.com/api/v1/agents/campaigns/${campaignId}/generate-posts`, + { + method: "POST", + headers: { + "Authorization": `Bearer ${process.env.PRODUCTCLANK_API_KEY}`, + }, + } + ); + + const result: GeneratePostsResponse = await response.json(); + + if (!result.success) { + throw new Error(`Generate posts failed: ${result.message}`); + } + + return result; +} +``` + +--- + +## Helper Utilities + +Useful helper functions. + +```typescript +// Validate campaign data before sending +function validateCampaignData(data: Partial): string[] { + const errors: string[] = []; + + if (!data.product_id?.trim()) { + errors.push("product_id is required"); + } + + if (!data.title?.trim()) { + errors.push("title is required"); + } + + if (!data.keywords || data.keywords.length === 0) { + errors.push("keywords must be a non-empty array"); + } + + if (!data.search_context?.trim()) { + errors.push("search_context is required"); + } + + return errors; +} + +// Usage +const errors = validateCampaignData(campaignData); +if (errors.length > 0) { + console.error("Validation errors:", errors); + throw new Error(errors.join(", ")); +} + +// Calculate bundle details +function getBundleDetails(bundle: CreditBundle): { credits: number; price: number } { + const bundles = { + nano: { credits: 40, price: 2 }, + micro: { credits: 200, price: 10 }, + small: { credits: 550, price: 25 }, + medium: { credits: 1200, price: 50 }, + large: { credits: 2600, price: 100 }, + enterprise: { credits: 14000, price: 500 }, + }; + return bundles[bundle]; +} + +// Estimate campaign cost +function estimateCampaignCost(estimatedPosts: number): number { + const CREDITS_PER_POST = 12; // Discovery + Reply + return estimatedPosts * CREDITS_PER_POST; +} + +// Recommend bundle based on estimated posts +function recommendBundle(estimatedPosts: number): CreditBundle { + const creditsNeeded = estimateCampaignCost(estimatedPosts); + + if (creditsNeeded <= 40) return "nano"; + if (creditsNeeded <= 200) return "micro"; + if (creditsNeeded <= 550) return "small"; + if (creditsNeeded <= 1200) return "medium"; + if (creditsNeeded <= 2600) return "large"; + return "enterprise"; +} + +// Format campaign URL +function getCampaignDashboardUrl(campaignId: string): string { + return `https://app.productclank.com/communiply/campaigns/${campaignId}`; +} +``` + +--- + +## Complete End-to-End Example + +Full workflow from user input to campaign creation. + +```typescript +import { wrapFetchWithPayment } from "@x402/fetch"; +import { createWalletClient, http } from "viem"; +import { base } from "viem/chains"; +import { privateKeyToAccount } from "viem/accounts"; + +async function main() { + // 1. Setup x402 payment + const account = privateKeyToAccount(process.env.AGENT_PRIVATE_KEY!); + const walletClient = createWalletClient({ + account, + chain: base, + transport: http(), + }); + const x402Fetch = wrapFetchWithPayment(fetch, walletClient); + + // 2. Gather campaign requirements (from user input, LLM, etc.) + const campaignData: CampaignRequest = { + product_id: "abc-123-def-456", + title: "AI Agents Launch Week", + keywords: [ + "AI agents", + "autonomous agents", + "agent frameworks", + "AI automation" + ], + search_context: "Developers and founders discussing AI agents, autonomous systems, and agent frameworks", + estimated_posts: 80, // Estimate for cost calculation + mention_accounts: ["@myaiagent", "@founder"], + reply_style_tags: ["technical", "enthusiastic", "helpful"], + reply_length: "short", + min_follower_count: 500, + max_post_age_days: 3, + }; + + // 3. Validate + const errors = validateCampaignData(campaignData); + if (errors.length > 0) { + throw new Error(`Validation failed: ${errors.join(", ")}`); + } + + // 4. Check credit balance and top up if needed + console.log("Checking credit balance..."); + const balanceResponse = await fetch( + "https://api.productclank.com/api/v1/credits/balance", + { + headers: { + "Authorization": `Bearer ${process.env.PRODUCTCLANK_API_KEY}`, + }, + } + ); + const { credits } = await balanceResponse.json(); + const estimatedCost = estimateCampaignCost(campaignData.estimated_posts || 50); + + console.log(`Current balance: ${credits} credits`); + console.log(`Estimated cost: ${estimatedCost} credits`); + + if (credits < estimatedCost) { + const recommendedBundle = recommendBundle(campaignData.estimated_posts || 50); + const bundleDetails = getBundleDetails(recommendedBundle); + console.log(`Topping up with ${recommendedBundle} bundle (+${bundleDetails.credits} credits)...`); + + await x402Fetch( + "https://api.productclank.com/api/v1/credits/topup", + { + method: "POST", + headers: { + "Authorization": `Bearer ${process.env.PRODUCTCLANK_API_KEY}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + bundle: recommendedBundle, + }), + } + ); + console.log("✅ Credits topped up successfully"); + } + + // 5. Create campaign (no credits deducted yet) + console.log("Creating campaign..."); + const response = await fetch( + "https://api.productclank.com/api/v1/agents/campaigns", + { + method: "POST", + headers: { + "Authorization": `Bearer ${process.env.PRODUCTCLANK_API_KEY}`, + "Content-Type": "application/json", + }, + body: JSON.stringify(campaignData), + } + ); + + const result: CampaignResponse = await response.json(); + + if (!result.success) { + throw new Error(`Failed: ${result.message}`); + } + + const campaignUrl = getCampaignDashboardUrl(result.campaign.id); + console.log(`✅ Campaign created: ${result.campaign.campaign_number}`); + console.log(`🔗 Review at: ${campaignUrl}`); + + // 6. (Optional) Share URL with user for review before generating posts + + // 7. Generate posts (credits deducted here) + console.log("Generating posts..."); + const generateResponse = await fetch( + `https://api.productclank.com/api/v1/agents/campaigns/${result.campaign.id}/generate-posts`, + { + method: "POST", + headers: { + "Authorization": `Bearer ${process.env.PRODUCTCLANK_API_KEY}`, + }, + } + ); + + const generateResult: GeneratePostsResponse = await generateResponse.json(); + + if (!generateResult.success) { + throw new Error(`Generate posts failed: ${generateResult.message}`); + } + + // 8. Return results to user + console.log(` +✅ Campaign Created Successfully! + +📋 Campaign Details: + - ID: ${result.campaign.campaign_number} + - Title: ${result.campaign.title} + - Status: ${result.campaign.status} + +📝 Posts Generated: + - Posts discovered: ${generateResult.postsGenerated} + - Replies generated: ${generateResult.repliesGenerated} + +💳 Credit Usage: + - Credits used: ${generateResult.credits.creditsUsed} + - Credits remaining: ${generateResult.credits.creditsRemaining} + +🔗 View Campaign: + ${campaignUrl} + +🎯 What Happens Next: + 1. Community members browse and claim reply opportunities + 2. They post replies from their personal accounts + 3. You track engagement and ROI in real-time + `); + + return result.campaign; +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error("Error:", error); + process.exit(1); + }); +``` + +--- + +For more examples and use cases, see: +- [SKILL.md](../SKILL.md) - Main skill documentation +- [API_REFERENCE.md](./API_REFERENCE.md) - Complete API reference +- [scripts/create-campaign.mjs](../scripts/create-campaign.mjs) - Ready-to-use script + +--- + +## Tier 2: Research-Enhanced Campaign (Coming Soon) + +Example code for the research-enhanced workflow. These endpoints are not yet available. + +```typescript +async function createResearchEnhancedCampaign() { + const API = "https://api.productclank.com/api/v1"; + const headers = { + "Authorization": `Bearer ${process.env.PRODUCTCLANK_API_KEY}`, + "Content-Type": "application/json", + }; + + // Step 1: Generate keywords from natural language (2 credits) + console.log("🔍 Generating keywords..."); + const keywordsRes = await fetch(`${API}/agents/generate-keywords`, { + method: "POST", + headers, + body: JSON.stringify({ + search_goals: "Content marketers looking for AI writing assistants", + product_name: "WriteAI", + product_tagline: "AI writing assistant for marketers", + }), + }); + const { keywords } = await keywordsRes.json(); + console.log(`✅ Generated ${keywords.length} keywords:`, keywords); + + // Step 2: Create campaign with AI-generated keywords (10 credits) + console.log("🚀 Creating campaign..."); + const campaignRes = await fetch(`${API}/agents/campaigns`, { + method: "POST", + headers, + body: JSON.stringify({ + product_id: "YOUR_PRODUCT_UUID", + title: "AI Writing Tools Campaign", + keywords, + search_context: "Marketers and content creators discussing AI writing tools", + reply_style_tags: ["helpful", "authentic"], + reply_length: "short", + }), + }); + const { campaign } = await campaignRes.json(); + console.log(`✅ Campaign created: ${campaign.campaign_number}`); + + // Step 3: Run research analysis (free) + console.log("🔬 Running research analysis..."); + const researchRes = await fetch( + `${API}/agents/campaigns/${campaign.id}/research`, + { method: "POST", headers } + ); + const { analysis } = await researchRes.json(); + console.log(`📊 Research complete:`); + console.log(` - ${analysis.expandedKeywords.length} expanded keywords`); + console.log(` - ${analysis.highIntentPhrases.length} high-intent phrases`); + console.log(` - ${analysis.twitterLists.length} Twitter lists found`); + console.log(` - ${analysis.competitors.length} competitors discovered`); + + // Step 4: Select discovery sources based on research (free) + console.log("⚙️ Configuring discovery sources..."); + await fetch(`${API}/agents/campaigns/${campaign.id}/verticals`, { + method: "POST", + headers, + body: JSON.stringify({ + enabledVerticals: ["keywords", "phrases", "lists"], + selectedTwitterListIds: analysis.twitterLists.slice(0, 5).map(l => l.id), + }), + }); + + // Step 5: Generate posts with enhanced targeting (12 credits/post) + console.log("⚡ Generating posts..."); + const generateRes = await fetch( + `${API}/agents/campaigns/${campaign.id}/generate-posts`, + { method: "POST", headers } + ); + const result = await generateRes.json(); + + console.log(` +✅ Research-Enhanced Campaign Live! + +📋 Campaign: ${campaign.campaign_number} +📝 Posts: ${result.postsGenerated} discovered, ${result.repliesGenerated} replies generated +💳 Credits: ${result.credits.creditsUsed} used, ${result.credits.creditsRemaining} remaining +🔗 View: https://app.productclank.com/communiply/${campaign.id} + `); + + return campaign; +} +``` + +--- + +## Tier 3: Iterate & Optimize (Coming Soon) + +Example code for the iterate and optimize workflow. These endpoints are not yet available. + +```typescript +async function iterateAndOptimizeCampaign(campaignId: string) { + const API = "https://api.productclank.com/api/v1"; + const headers = { + "Authorization": `Bearer ${process.env.PRODUCTCLANK_API_KEY}`, + "Content-Type": "application/json", + }; + + // Step 1: Read generated posts and replies (free) + console.log("📖 Reading campaign results..."); + const postsRes = await fetch( + `${API}/agents/campaigns/${campaignId}/posts?includeReplies=true&limit=20`, + { headers } + ); + const { posts, total, availableTotal } = await postsRes.json(); + console.log(`📊 ${total} posts total, ${availableTotal} available for community`); + + // Analyze results + const avgRelevance = posts.reduce((sum, p) => sum + p.relevanceScore, 0) / posts.length; + console.log(`📈 Average relevance score: ${(avgRelevance * 100).toFixed(1)}%`); + + // Step 2: Use AI refine to optimize (3 credits/message) + console.log("🤖 Asking AI for optimization suggestions..."); + const refineRes = await fetch( + `${API}/agents/campaigns/${campaignId}/refine`, + { + method: "POST", + headers, + body: JSON.stringify({ + messages: [{ + role: "user", + content: "The replies are too formal and long. Make them shorter, more casual, and focus on personal experience rather than features.", + }], + }), + } + ); + const refineResult = await refineRes.json(); + console.log(`💬 AI: ${refineResult.message}`); + console.log(`⚡ Actions executed: ${refineResult.actions_executed.length}`); + + // Step 3: Regenerate replies for top posts (5 credits/reply) + const topPostIds = posts.slice(0, 5).map(p => p.id); + console.log(`🔄 Regenerating replies for top ${topPostIds.length} posts...`); + const regenRes = await fetch( + `${API}/agents/campaigns/${campaignId}/regenerate-replies`, + { + method: "POST", + headers, + body: JSON.stringify({ + postIds: topPostIds, + editRequest: "Shorter, more casual, share personal experience", + }), + } + ); + const regenResult = await regenRes.json(); + console.log(`✅ ${regenResult.repliesGenerated} replies regenerated`); + + // Step 4: Generate more posts (12 credits/post) + console.log("⚡ Generating more posts with updated style..."); + const moreRes = await fetch( + `${API}/agents/campaigns/${campaignId}/generate-posts`, + { method: "POST", headers } + ); + const moreResult = await moreRes.json(); + console.log(`✅ ${moreResult.postsGenerated} new posts generated`); + + // Step 5: Read updated campaign stats (free) + const campaignRes = await fetch( + `${API}/agents/campaigns/${campaignId}`, + { headers } + ); + const { campaign } = await campaignRes.json(); + + console.log(` +📊 Campaign Stats After Optimization: + Posts found: ${campaign.total_posts_found} + Replies generated: ${campaign.total_replies_generated} + Community participations: ${campaign.total_participations} + `); + + return campaign; +} + +// Usage: iterate on an existing campaign +// iterateAndOptimizeCampaign("your-campaign-uuid"); +``` diff --git a/productclank/references/FAQ.md b/productclank/references/FAQ.md new file mode 100644 index 00000000..b7da5a6a --- /dev/null +++ b/productclank/references/FAQ.md @@ -0,0 +1,45 @@ +# ProductClank Agent API — Frequently Asked Questions + +## Getting Started + +**Q: Do I need to contact anyone to get an API key?** +A: No. Self-register via `POST /api/v1/agents/register`. API key + 300 free credits are provided instantly. + +**Q: Do I need USDC to start?** +A: No. Registration includes 300 free credits — enough for ~24 posts. Buy more when they run out. + +**Q: Is there a test environment?** +A: No separate test API — use the 300 free credits from registration to test on production. + +## Campaigns + +**Q: What happens after a campaign is created?** +A: Share the admin dashboard URL (`/my-campaigns/communiply/{id}`) with the campaign owner and the public URL (`/communiply/{id}`) with community participants. Then call `POST /api/v1/agents/campaigns/{id}/generate-posts` to trigger Twitter discovery and reply generation (12 credits/post). Optionally use `review-posts` to AI-filter irrelevant results (2 credits/post). + +**Q: How much does it cost to create a campaign?** +A: 10 credits for campaign creation + 12 credits per post discovered. A typical 10-post test campaign costs ~130 credits. + +**Q: Can I list or check campaigns via API?** +A: Yes. `GET /api/v1/agents/campaigns` lists all campaigns. `GET /api/v1/agents/campaigns/{id}` shows details and stats. + +**Q: Can I delete or pause campaigns?** +A: Yes, via the admin dashboard at `https://app.productclank.com/my-campaigns/communiply/{campaign_id}` + +**Q: Which endpoint — Communiply or Boost?** +A: Communiply for ongoing keyword-based monitoring. Boost for amplifying a specific tweet immediately. See the decision tree in SKILL.md. + +## Agent Setup + +**Q: What's the difference between autonomous and owner-linked agents?** +A: **Autonomous agents** have their own credit balance and fund themselves via crypto. **Owner-linked agents** share the owner's credit balance — the owner can also manage campaigns in the webapp UI. + +**Q: How do I link my agent to my account?** +A: Call `POST /api/v1/agents/create-link` to get a linking URL. Click it, log in via Privy, and the agent is linked. + +## Account Management + +**Q: What if I lose my API key?** +A: Use `POST /api/v1/agents/rotate-key` with the current key to generate a new one. If access is lost completely, contact ProductClank. + +**Q: How do I increase rate limits?** +A: Contact ProductClank with use case and expected volume. diff --git a/productclank/references/FUNDING.md b/productclank/references/FUNDING.md new file mode 100644 index 00000000..489fb79a --- /dev/null +++ b/productclank/references/FUNDING.md @@ -0,0 +1,146 @@ +# ProductClank — Credit Funding Guide + +## Who Pays? + +- **Autonomous agents** — credits deducted from the agent's own balance (funded via crypto) +- **Owner-linked agents** — credits deducted from the linked owner's balance (funded via webapp or crypto) + +## Credit Bundles (USDC on Base) + +| Bundle | Price | Credits | Rate | ~Posts | +|--------|-------|---------|------|--------| +| **nano** | $2 | 40 | 20 cr/$ | ~3 | +| **micro** | $10 | 200 | 20 cr/$ | ~16 | +| **small** | $25 | 550 | 22 cr/$ | ~45 | +| **medium** | $50 | 1,200 | 24 cr/$ | ~100 | +| **large** | $100 | 2,600 | 26 cr/$ | ~216 | +| **enterprise** | $500 | 14,000 | 28 cr/$ | ~1,166 | + +## Credit Cost Summary (All Tiers) + +| Operation | Credits | Tier | +|-----------|---------|------| +| Create campaign | 10 | 1 | +| Generate posts (discover + reply) | 12/post | 1 | +| Generate reply only | 8 | 1 | +| Regenerate reply | 5 | 1 | +| Review post (AI relevancy) | 2/post | 1 | +| Tweet boost (replies) | 200 | 1 | +| Tweet boost (likes/repost) | 300 | 1 | +| Generate keywords (AI) | 2 | 2 | +| Research analysis | 0 (free) | 2 | +| Read campaign/posts | 0 (free) | 3 | +| Regenerate replies | 5/reply | 3 | +| Refine chat (AI) | 3/message | 3 | +| Update settings | 0 (free) | 3 | + +## Scenario 1: Autonomous Agent (Self-Funded) + +Agent manages its own credit balance independently. + +### Option A: x402 Protocol (Recommended) + +Requires a wallet with private key access + USDC on Base. Payment happens automatically. + +```typescript +import { wrapFetchWithPayment } from "@x402/fetch"; +const x402Fetch = wrapFetchWithPayment(fetch, walletClient); + +const topup = await x402Fetch( + "https://api.productclank.com/api/v1/agents/credits/topup", + { + method: "POST", + headers: { "Authorization": "Bearer pck_live_YOUR_KEY", "Content-Type": "application/json" }, + body: JSON.stringify({ bundle: "medium" }) + } +); +``` + +### Option B: Direct USDC Transfer + +1. Send exact USDC amount on Base to `0x876Be690234aaD9C7ae8bb02c6900f5844aCaF68` +2. Call `POST /api/v1/agents/credits/topup` with `{ bundle: "medium", payment_tx_hash: "0x..." }` +3. Transaction must be < 1 hour old; each tx hash is single-use + +## Scenario 2: Agent Running Campaigns for Users (User-Funded) + +Agent creates campaigns on behalf of users, who pay for the credits. + +### Step 1: User Authorizes the Agent + +```bash +# Agent generates a linking URL +curl -X POST "https://api.productclank.com/api/v1/agents/create-link" \ + -H "Authorization: Bearer pck_live_YOUR_AGENT_API_KEY" +``` + +Share the returned `link_url` with the user. They click it, log in via Privy, and authorize the agent to use their credits. + +### Step 2: User Tops Up Credits + +Direct the user to: **https://app.productclank.com/credits** + +User payment options: +- **Credit card** — No crypto needed +- **Crypto** — USDC on Base +- **Monthly subscription** — Better rates per credit, auto-renewal + +### Step 3: Agent Uses User's Credits + +Once authorized, pass `caller_user_id` to bill the user's balance: + +```bash +curl -X POST "https://api.productclank.com/api/v1/agents/campaigns/{id}/generate-posts" \ + -H "Authorization: Bearer pck_live_YOUR_AGENT_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{"caller_user_id": "user-uuid-here"}' +``` + +The user manages credits and billing through the webapp dashboard. + +## Checking Balance & History + +```bash +# Check balance +curl https://api.productclank.com/api/v1/agents/credits/balance \ + -H "Authorization: Bearer pck_live_YOUR_KEY" + +# Transaction history +curl "https://api.productclank.com/api/v1/agents/credits/history?limit=50" \ + -H "Authorization: Bearer pck_live_YOUR_KEY" +``` + +## Payment Details + +- **Network:** Base (chain ID 8453) +- **Payment Address:** `0x876Be690234aaD9C7ae8bb02c6900f5844aCaF68` +- **USDC Contract:** `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` +- **x402 Protocol:** v2 with EIP-3009 `TransferWithAuthorization` + +## Upcoming Tiers + +### Tier 2: Research-Enhanced Campaign + +Enhance campaigns with AI-powered research before generating posts. + +``` +1. POST /agents/generate-keywords → 2 credits +2. POST /agents/campaigns → 10 credits +3. POST /agents/campaigns/{id}/research → free +4. GET /agents/campaigns/{id}/research → free +5. POST /agents/campaigns/{id}/verticals → free +6. POST /agents/campaigns/{id}/generate-posts → 12 credits/post +``` + +### Tier 3: Iterate & Optimize + +Full campaign lifecycle management with AI refinement. + +``` +7. GET /agents/campaigns/{id}/posts → free +8. POST /agents/campaigns/{id}/refine → 3 credits/message +9. POST /agents/campaigns/{id}/regenerate-replies → 5 credits/reply +10. PATCH /agents/campaigns/{id} → free +11. POST /agents/campaigns/{id}/generate-posts → 12 credits/post +12. Repeat 7-11 as needed +``` diff --git a/productclank/scripts/create-campaign.mjs b/productclank/scripts/create-campaign.mjs new file mode 100755 index 00000000..45ff2104 --- /dev/null +++ b/productclank/scripts/create-campaign.mjs @@ -0,0 +1,348 @@ +#!/usr/bin/env node + +/** + * ProductClank Campaign Creation Script + * + * A helper script for creating Communiply campaigns via the ProductClank Agent API. + * Supports both x402 protocol payment and direct USDC transfers. + * + * Usage: + * node create-campaign.mjs + * + * Environment Variables Required: + * PRODUCTCLANK_API_KEY - Your API key (pck_live_*) + * AGENT_PRIVATE_KEY - Your wallet private key (for x402 payment) + * + * OR for direct transfer: + * PRODUCTCLANK_API_KEY - Your API key + * PAYMENT_TX_HASH - Transaction hash of USDC transfer + */ + +import { createWalletClient, http } from "viem"; +import { base } from "viem/chains"; +import { privateKeyToAccount } from "viem/accounts"; +import { wrapFetchWithPayment } from "@x402/fetch"; + +// Configuration +const API_BASE_URL = "https://api.productclank.com/api/v1"; +const API_KEY = process.env.PRODUCTCLANK_API_KEY; +const PRIVATE_KEY = process.env.AGENT_PRIVATE_KEY; +const PAYMENT_TX_HASH = process.env.PAYMENT_TX_HASH; + +// Validation +if (!API_KEY) { + console.error("❌ Error: PRODUCTCLANK_API_KEY environment variable is required"); + console.error("Set it with: export PRODUCTCLANK_API_KEY=pck_live_YOUR_KEY"); + process.exit(1); +} + +if (!PRIVATE_KEY && !PAYMENT_TX_HASH) { + console.error("❌ Error: Either AGENT_PRIVATE_KEY or PAYMENT_TX_HASH is required"); + console.error("For x402 payment: export AGENT_PRIVATE_KEY=0xYOUR_PRIVATE_KEY"); + console.error("For direct transfer: export PAYMENT_TX_HASH=0xYOUR_TX_HASH"); + process.exit(1); +} + +// Example campaign data - modify this for your use case +const campaignData = { + product_id: "YOUR_PRODUCT_UUID", // ⚠️ Replace with your product ID + title: "Example Campaign", + keywords: [ + "AI tools", + "productivity apps", + "automation software" + ], + search_context: "People discussing AI productivity tools and automation challenges", + estimated_posts: 4, // 4 posts × 12 credits = 48 credits (fits in nano bundle) + mention_accounts: ["@productclank"], + reply_style_tags: ["friendly", "helpful"], + reply_length: "short", + min_follower_count: 100, + max_post_age_days: 7, +}; + +// Validate campaign data +function validateCampaignData(data) { + const errors = []; + + if (!data.product_id || data.product_id === "YOUR_PRODUCT_UUID") { + errors.push("product_id must be set to a valid UUID"); + } + + if (!data.title?.trim()) { + errors.push("title is required"); + } + + if (!data.keywords || data.keywords.length === 0) { + errors.push("keywords must be a non-empty array"); + } + + if (!data.search_context?.trim()) { + errors.push("search_context is required"); + } + + return errors; +} + +// Check credit balance +async function checkCreditBalance() { + const response = await fetch(`${API_BASE_URL}/credits/balance`, { + headers: { + "Authorization": `Bearer ${API_KEY}`, + }, + }); + return response.json(); +} + +// Top up credits using x402 protocol +async function topUpCreditsWithX402(bundle) { + console.log(`💳 Topping up credits with ${bundle} bundle using x402 protocol...`); + + const account = privateKeyToAccount(PRIVATE_KEY); + const walletClient = createWalletClient({ + account, + chain: base, + transport: http(), + }); + + const x402Fetch = wrapFetchWithPayment(fetch, walletClient); + + const response = await x402Fetch(`${API_BASE_URL}/credits/topup`, { + method: "POST", + headers: { + "Authorization": `Bearer ${API_KEY}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ bundle }), + }); + + return response.json(); +} + +// Create campaign (no credits deducted at this step) +async function createCampaign(data) { + console.log("🔨 Creating campaign..."); + + const response = await fetch(`${API_BASE_URL}/agents/campaigns`, { + method: "POST", + headers: { + "Authorization": `Bearer ${API_KEY}`, + "Content-Type": "application/json", + }, + body: JSON.stringify(data), + }); + + return response.json(); +} + +// Generate posts for a campaign (credits deducted here) +async function generatePosts(campaignId) { + console.log("⚡ Generating posts (credits will be deducted)..."); + + const response = await fetch( + `${API_BASE_URL}/agents/campaigns/${campaignId}/generate-posts`, + { + method: "POST", + headers: { + "Authorization": `Bearer ${API_KEY}`, + }, + } + ); + + return response.json(); +} + +// Top up credits using direct USDC transfer +async function topUpCreditsWithDirectTransfer(bundle) { + console.log("💸 Using direct USDC transfer for credit top-up..."); + console.log(`📜 Transaction hash: ${PAYMENT_TX_HASH}`); + + const response = await fetch(`${API_BASE_URL}/credits/topup`, { + method: "POST", + headers: { + "Authorization": `Bearer ${API_KEY}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + bundle, + payment_tx_hash: PAYMENT_TX_HASH, + }), + }); + + return response.json(); +} + +// Recommend bundle based on estimated posts +function recommendBundle(estimatedPosts) { + const creditsNeeded = estimatedPosts * 12; // 12 credits per post + + if (creditsNeeded <= 40) return "nano"; + if (creditsNeeded <= 200) return "micro"; + if (creditsNeeded <= 550) return "small"; + if (creditsNeeded <= 1200) return "medium"; + if (creditsNeeded <= 2600) return "large"; + return "enterprise"; +} + +// Get bundle details +function getBundleDetails(bundle) { + const bundles = { + nano: { credits: 40, price: 2 }, + micro: { credits: 200, price: 10 }, + small: { credits: 550, price: 25 }, + medium: { credits: 1200, price: 50 }, + large: { credits: 2600, price: 100 }, + enterprise: { credits: 14000, price: 500 }, + }; + return bundles[bundle]; +} + +// Main execution +async function main() { + console.log("🚀 ProductClank Campaign Creation Script\n"); + + // Validate campaign data + console.log("✅ Validating campaign data..."); + const errors = validateCampaignData(campaignData); + if (errors.length > 0) { + console.error("❌ Validation errors:"); + errors.forEach(err => console.error(` - ${err}`)); + console.error("\n💡 Edit the campaignData object in this script to fix these errors."); + process.exit(1); + } + + // Display campaign details + const estimatedPosts = campaignData.estimated_posts || 50; + const estimatedCredits = estimatedPosts * 12; + const recommendedBundle = recommendBundle(estimatedPosts); + const bundleDetails = getBundleDetails(recommendedBundle); + + console.log("📋 Campaign Details:"); + console.log(` - Title: ${campaignData.title}`); + console.log(` - Keywords: ${campaignData.keywords.join(", ")}`); + console.log(` - Estimated Posts: ${estimatedPosts}`); + console.log(` - Estimated Credits: ~${estimatedCredits}`); + console.log(` - Recommended Bundle: ${recommendedBundle} ($${bundleDetails.price} = ${bundleDetails.credits} credits)`); + console.log(""); + + // Check credit balance + console.log("💳 Checking credit balance..."); + try { + const balanceData = await checkCreditBalance(); + console.log(` Current balance: ${balanceData.credits} credits`); + + // Top up if needed + if (balanceData.credits < estimatedCredits) { + console.log(`\n⚠️ Insufficient credits (need ${estimatedCredits}, have ${balanceData.credits})`); + console.log(` Topping up with ${recommendedBundle} bundle...`); + + let topupResult; + if (PAYMENT_TX_HASH) { + topupResult = await topUpCreditsWithDirectTransfer(recommendedBundle); + } else { + topupResult = await topUpCreditsWithX402(recommendedBundle); + } + + if (topupResult.success) { + console.log(`\n✅ Credits Topped Up!`); + console.log(` Added: ${topupResult.credits_added} credits`); + console.log(` New balance: ${topupResult.new_balance} credits`); + console.log(` Amount paid: $${topupResult.amount_usdc} USDC`); + console.log(""); + } else { + console.error(`\n❌ Top-up Failed: ${topupResult.error}`); + console.error(` Message: ${topupResult.message}`); + process.exit(1); + } + } else { + console.log(` ✅ Sufficient credits available\n`); + } + + // Create campaign (no credits deducted yet) + const result = await createCampaign(campaignData); + + if (!result.success) { + console.error(`\n❌ Campaign Creation Failed\n`); + console.error(`Error: ${result.error}`); + console.error(`Message: ${result.message}`); + + if (result.error === "insufficient_credits" && result.topup_options) { + console.error("\n💡 Insufficient Credits:"); + console.error(` Required: ${result.required_credits} credits`); + console.error(` Available: ${result.available_credits} credits`); + console.error("\n Top-up Options:"); + result.topup_options.forEach(option => { + const marker = option.recommended ? " (recommended)" : ""; + console.error(` - ${option.bundle}: ${option.credits} credits for $${option.price}${marker}`); + }); + } else if (result.error === "rate_limit_exceeded") { + console.error("\n💡 Rate limit exceeded. Try again tomorrow or contact ProductClank for higher limits."); + } else if (result.error === "unauthorized") { + console.error("\n💡 Invalid API key. Verify PRODUCTCLANK_API_KEY is correct."); + } else if (result.error === "not_found") { + console.error("\n💡 Product not found. Verify product_id exists on ProductClank."); + console.error(" Visit: https://app.productclank.com/products"); + } + + process.exit(1); + } + + // Campaign created — share URL for review + const campaignUrl = `https://app.productclank.com/communiply/campaigns/${result.campaign.id}`; + console.log("\n✅ Campaign Created!\n"); + console.log("📋 Campaign Details:"); + console.log(` - ID: ${result.campaign.campaign_number}`); + console.log(` - Title: ${result.campaign.title}`); + console.log(` - Status: ${result.campaign.status}`); + console.log(""); + console.log("🔗 Review Campaign (optional — share with user before generating posts):"); + console.log(` ${campaignUrl}`); + console.log(""); + + // Generate posts (credits deducted here) + const generateResult = await generatePosts(result.campaign.id); + + if (generateResult.success) { + console.log("\n✅ Posts Generated Successfully!\n"); + console.log("📝 Generation Results:"); + console.log(` - Posts discovered: ${generateResult.postsGenerated}`); + console.log(` - Replies generated: ${generateResult.repliesGenerated}`); + console.log(""); + console.log("💳 Credit Usage:"); + console.log(` - Credits used: ${generateResult.credits.creditsUsed}`); + console.log(` - Credits remaining: ${generateResult.credits.creditsRemaining}`); + console.log(""); + console.log("🔗 View Campaign:"); + console.log(` ${campaignUrl}`); + console.log(""); + console.log("🎯 Next Steps:"); + console.log(" 1. Community can browse and claim reply opportunities"); + console.log(" 2. They post replies from their personal accounts"); + console.log(" 3. Track engagement in real-time via dashboard"); + console.log(""); + } else { + console.error(`\n❌ Generate Posts Failed\n`); + console.error(`Error: ${generateResult.error}`); + console.error(`Message: ${generateResult.message}`); + + if (generateResult.error === "insufficient_credits") { + console.error("\n💡 Insufficient credits. Top up via /api/v1/agents/credits/topup then retry generate-posts."); + console.error(` Campaign URL: ${campaignUrl}`); + console.error(` Generate-posts endpoint: POST /api/v1/agents/campaigns/${result.campaign.id}/generate-posts`); + } + + process.exit(1); + } + } catch (error) { + console.error("\n❌ Error:", error.message); + console.error("\nStack trace:"); + console.error(error.stack); + process.exit(1); + } +} + +// Run script +main().catch(error => { + console.error("Fatal error:", error); + process.exit(1); +});