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 CategoryLimit
Auth (login, register)10 requests/minute
API (general)60 requests/minute
Webhooks30 requests/minute
Agent runs10 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 -- Success
  • 201 -- Created (new resource)
  • 204 -- No content (logout, delete)
  • 400 -- Validation error
  • 401 -- Unauthorized (missing or invalid token)
  • 402 -- Insufficient credits
  • 403 -- Forbidden (wrong role or org)
  • 404 -- Not found
  • 429 -- Rate limited