API Quickstart
This guide covers the most common API operations with curl examples. The API runs on Cloudflare Workers with Hono and returns JSON responses.
Base URL:
- Local development:
http://localhost:8788/api - Production:
https://your-domain.com/api
Authentication
Dev Login (development only)
curl -X POST http://localhost:8788/api/auth/dev-login
Response:
{
"data": {
"access_token": "eyJhbGciOi...",
"user": {
"id": "user_abc123",
"org_id": "org_upland",
"email": "dev@upland.agency",
"name": "Dev Admin",
"role": "super_admin"
}
}
}
Save the access token:
TOKEN=$(curl -s -X POST http://localhost:8788/api/auth/dev-login | jq -r '.data.access_token')
Register a New User
curl -X POST http://localhost:8788/api/auth/register \
-H "Content-Type: application/json" \
-d '{
"email": "keenan@upland.agency",
"password": "securepassword123",
"name": "Keenan",
"org_name": "Upland Marketing",
"org_type": "agency",
"plan": "enterprise",
"vertical": "other"
}'
Login
curl -X POST http://localhost:8788/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "keenan@upland.agency",
"password": "securepassword123"
}'
Get Current User Profile
curl http://localhost:8788/api/auth/me \
-H "Authorization: Bearer $TOKEN"
Refresh Token
Access tokens expire. Refresh them using the httpOnly cookie set at login:
curl -X POST http://localhost:8788/api/auth/refresh \
-H "Cookie: refresh_token=your-refresh-token-here"
Logout
curl -X POST http://localhost:8788/api/auth/logout \
-H "Authorization: Bearer $TOKEN"
List Clients (Organizations)
List All Organizations
curl http://localhost:8788/api/orgs \
-H "Authorization: Bearer $TOKEN"
Response:
{
"data": [
{
"id": "org_upland",
"name": "Upland Marketing",
"type": "agency",
"plan": "enterprise",
"credits_remaining": 9999,
"credits_monthly": 9999,
"is_active": 1
},
{
"id": "org_hopthief",
"name": "The Hop Thief",
"type": "client",
"plan": "standard",
"vertical": "bar",
"credits_remaining": 400,
"credits_monthly": 400,
"is_active": 1
}
],
"total_count": 3
}
Get a Single Organization
curl http://localhost:8788/api/orgs/org_hopthief \
-H "Authorization: Bearer $TOKEN"
Create a New Client Organization
curl -X POST http://localhost:8788/api/orgs \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "River City Coffee",
"plan": "starter",
"vertical": "coffee"
}'
Trigger an Agent Run
Run a Single Agent
curl -X POST http://localhost:8788/api/agents/orgs/org_hopthief/social_post/run \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"input": {
"topic": "Friday happy hour specials",
"platforms": ["instagram", "facebook"],
"mood": "casual and inviting"
}
}'
Response:
{
"data": {
"run_ids": ["agent_run_xyz789"],
"variant_group_id": null,
"variants": 1,
"status": "queued"
}
}
Run with A/B Variants
Generate 2-3 variants of the same content for comparison:
curl -X POST http://localhost:8788/api/agents/orgs/org_hopthief/social_post/run \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"input": {
"topic": "New spring cocktail menu",
"platforms": ["instagram"]
},
"variants": 3
}'
Response:
{
"data": {
"run_ids": ["agent_run_a", "agent_run_b", "agent_run_c"],
"variant_group_id": "variant_group_abc",
"variants": 3,
"status": "queued"
}
}
When you approve one variant, the others are automatically rejected.
Check Run Status
Get a Single Run
curl http://localhost:8788/api/agents/runs/agent_run_xyz789 \
-H "Authorization: Bearer $TOKEN"
Response:
{
"data": {
"id": "agent_run_xyz789",
"org_id": "org_hopthief",
"agent_type": "social_post",
"status": "awaiting_review",
"trigger_type": "manual",
"model_used": "claude-sonnet-4-6",
"credits_used": 3,
"duration_ms": 4523,
"output": "{\"content\":[...],\"summary\":\"...\"}",
"content_items": [
{
"id": "content_item_abc",
"content_type": "post",
"platform": "instagram",
"body": "Generated caption text...",
"hashtags": "[\"#hopthief\",\"#wallawalla\"]",
"status": "awaiting_review"
}
]
}
}
List Runs for an Organization
# All runs
curl "http://localhost:8788/api/agents/orgs/org_hopthief/runs" \
-H "Authorization: Bearer $TOKEN"
# Filter by agent type
curl "http://localhost:8788/api/agents/orgs/org_hopthief/runs?agent_type=social_post" \
-H "Authorization: Bearer $TOKEN"
# Filter by status
curl "http://localhost:8788/api/agents/orgs/org_hopthief/runs?status=awaiting_review" \
-H "Authorization: Bearer $TOKEN"
# Limit results
curl "http://localhost:8788/api/agents/orgs/org_hopthief/runs?limit=10" \
-H "Authorization: Bearer $TOKEN"
View the Review Queue (Agency-Wide)
curl http://localhost:8788/api/agents/review-queue \
-H "Authorization: Bearer $TOKEN"
Returns all runs with status: 'awaiting_review' across all client orgs, sorted by variant groups and creation time.
Approve or Reject Content
Approve a Run
curl -X POST http://localhost:8788/api/agents/runs/agent_run_xyz789/review \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"action": "approved",
"notes": "Great caption, publishing as-is"
}'
When approved:
- The run status changes to
approved - All content items from this run are set to
approved - Content items are queued for publishing (if platform connections exist)
- If this run is part of a variant group, sibling variants are automatically rejected
Reject a Run
curl -X POST http://localhost:8788/api/agents/runs/agent_run_xyz789/review \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"action": "rejected",
"notes": "Tone is too formal for this client. Re-run with mood set to casual."
}'
Edit and Approve
curl -X POST http://localhost:8788/api/agents/runs/agent_run_xyz789/review \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"action": "edited",
"notes": "Fixed the happy hour time from 4-6 to 3-6"
}'
Note: The edited action results in approved status. To update the actual content text, edit the content item directly (see below).
Content Management
List Content for an Organization
# All content
curl "http://localhost:8788/api/content/orgs/org_hopthief" \
-H "Authorization: Bearer $TOKEN"
# Filter by status
curl "http://localhost:8788/api/content/orgs/org_hopthief?status=approved" \
-H "Authorization: Bearer $TOKEN"
# Filter by platform
curl "http://localhost:8788/api/content/orgs/org_hopthief?platform=instagram" \
-H "Authorization: Bearer $TOKEN"
# Filter by type
curl "http://localhost:8788/api/content/orgs/org_hopthief?type=post" \
-H "Authorization: Bearer $TOKEN"
Edit a Content Item
curl -X PUT http://localhost:8788/api/content/content_item_abc \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"body": "Updated caption text with corrected happy hour time",
"hashtags": ["#hopthief", "#wallawalla", "#happyhour"]
}'
Editable fields: title, body, hashtags, media_urls, scheduled_for, platform.
Schedule Content
curl -X POST http://localhost:8788/api/content/content_item_abc/schedule \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"scheduled_for": "2026-03-22T18:00:00Z",
"platform": "instagram"
}'
View Content Calendar
curl "http://localhost:8788/api/content/orgs/org_hopthief/calendar?start=2026-03-01&end=2026-03-31" \
-H "Authorization: Bearer $TOKEN"
Brand Kits
Get a Client's Brand Kit
curl http://localhost:8788/api/brand-kits/org_hopthief \
-H "Authorization: Bearer $TOKEN"
Update a Brand Kit
curl -X PUT http://localhost:8788/api/brand-kits/org_hopthief \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"business_name": "The Hop Thief",
"tagline": "Craft Cocktails & Local Brews",
"industry": "Bar & Restaurant",
"vertical": "bar",
"location_city": "Walla Walla",
"location_state": "WA",
"location_neighborhood": "Downtown",
"voice_description": "Witty, warm, knowledgeable about craft drinks. Like your favorite bartender who always has a story.",
"voice_examples": "[\"Just tapped something special...\", \"Tuesday called. It wants you to come try our new menu.\"]",
"target_audience": "21-45 year olds who appreciate craft cocktails and local beer",
"products": "[\"Smoked Old Fashioned\", \"Hop Thief IPA\", \"Charcuterie Board\"]",
"competitors": "[\"Whitehouse-Crawford\", \"Public House 124\"]",
"unique_selling_points": "[\"Only craft cocktail bar in downtown WW\", \"Rotating local tap list\", \"Tableside smoked cocktails\"]",
"content_pillars": "[\"cocktail craftsmanship\", \"local community\", \"behind the bar\", \"weekly specials\"]",
"hashtag_sets": "{\"branded\": [\"#thehopthief\", \"#hopthiefww\"], \"local\": [\"#wallawalla\", \"#downtownww\"], \"industry\": [\"#craftcocktails\", \"#mixology\"]}",
"website_url": "https://thehopthief.com",
"social_handles": "{\"instagram\": \"@thehopthief\", \"facebook\": \"TheHopThiefWW\"}"
}'
Agent Configuration
List Agent Configs for an Organization
curl http://localhost:8788/api/agents/orgs/org_hopthief \
-H "Authorization: Bearer $TOKEN"
Enable/Configure an Agent
curl -X PUT http://localhost:8788/api/agents/orgs/org_hopthief/email_campaign \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"is_enabled": true,
"review_mode": "agency_review",
"platforms": ["email"],
"custom_instructions": "Always mention happy hour in promotional emails"
}'
Get the Agent Catalog
curl http://localhost:8788/api/agents/catalog \
-H "Authorization: Bearer $TOKEN"
Returns all 21 agent templates with name, description, category, credit cost, and tier requirements.
View Agent Anatomy (Admin)
curl http://localhost:8788/api/agents/anatomy/social_post \
-H "Authorization: Bearer $TOKEN"
Returns the full internal details: live system prompt, quality preamble, model config, credit costs, usage stats.
Analytics
Agency Overview
curl http://localhost:8788/api/analytics/overview \
-H "Authorization: Bearer $TOKEN"
Per-Organization Analytics
curl http://localhost:8788/api/analytics/orgs/org_hopthief \
-H "Authorization: Bearer $TOKEN"
Admin Dashboard
curl http://localhost:8788/api/admin/dashboard \
-H "Authorization: Bearer $TOKEN"
Returns cross-org stats: total runs, content items, credit usage, recent activity.
Common Workflows
Full Content Generation Pipeline
TOKEN=$(curl -s -X POST http://localhost:8788/api/auth/dev-login | jq -r '.data.access_token')
ORG="org_hopthief"
# 1. Generate a content calendar for the month
curl -X POST "http://localhost:8788/api/agents/orgs/$ORG/content_calendar/run" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"input": {
"month": "April 2026",
"posts_per_week": 4,
"focus_areas": ["spring cocktail menu", "patio season"]
}
}'
# 2. Wait for it to complete, then check the review queue
sleep 15
curl "http://localhost:8788/api/agents/review-queue" \
-H "Authorization: Bearer $TOKEN" | jq '.data[] | {id, agent_type: .agent_type, org_name: .org_name}'
# 3. Approve the calendar (get the run_id from the review queue)
curl -X POST "http://localhost:8788/api/agents/runs/CALENDAR_RUN_ID/review" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"action": "approved"}'
# 4. The calendar chain auto-triggers social_post runs
# Check for chained runs
sleep 10
curl "http://localhost:8788/api/agents/orgs/$ORG/runs?agent_type=social_post&limit=5" \
-H "Authorization: Bearer $TOKEN" | jq '.data[] | {id, status, trigger: .trigger_type}'
Prospect Outreach Pipeline
TOKEN=$(curl -s -X POST http://localhost:8788/api/auth/dev-login | jq -r '.data.access_token')
AGENCY_ORG="org_upland" # Agency tools run on the agency org
# 1. Run prospect audit
curl -X POST "http://localhost:8788/api/agents/orgs/$AGENCY_ORG/prospect_audit/run" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"input": {
"business_name": "Basin City Brewing",
"business_type": "Craft brewery and taproom",
"location": "Richland, WA",
"social_handles": "@basincitybrewing",
"notes": "Good product but only posting every 2 weeks"
}
}'
# 2. Generate demo content
curl -X POST "http://localhost:8788/api/agents/orgs/$AGENCY_ORG/demo_content/run" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"input": {
"business_name": "Basin City Brewing",
"business_type": "Craft brewery",
"location": "Richland, WA",
"vibe": "casual",
"known_products": "Nuke Juice IPA, Atomic Amber, weekend food trucks"
}
}'
# 3. Generate cold outreach (after reviewing the audit)
curl -X POST "http://localhost:8788/api/agents/orgs/$AGENCY_ORG/cold_outreach/run" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"input": {
"business_name": "Basin City Brewing",
"owner_name": "Jake",
"business_type": "Craft brewery",
"location": "Richland, WA",
"pain_points": "Inconsistent posting, no review responses",
"audit_highlights": "Social media 3/10, review management 2/10",
"hook": "free marketing audit + sample content"
}
}'
Rate Limits
| Endpoint Category | Limit |
|---|---|
| Auth (login, register) | 10 requests/minute |
| API (general) | 60 requests/minute |
| Webhooks | 30 requests/minute |
| Agent runs | 10 requests/minute |
Rate limits are per IP address, enforced via Cloudflare KV. Exceeding a limit returns 429 Too Many Requests.
Response Format
All API responses follow a consistent format:
Success:
{
"data": { ... }
}
List:
{
"data": [ ... ],
"total_count": 42
}
Error:
{
"error": {
"code": "NOT_FOUND",
"message": "Agent run not found"
}
}
Status codes:
200-- Success201-- Created (new resource)204-- No content (logout, delete)400-- Validation error401-- Unauthorized (missing or invalid token)402-- Insufficient credits403-- Forbidden (wrong role or org)404-- Not found429-- Rate limited