{
  "schema_version": "1.0",
  "name": "320px",
  "description": "Agent-driven marketing automation. Contacts, campaigns, segments and analytics exposed as MCP tools. Cloudflare-native. Multi-tenant: the active organization is implicit from the bearer API key, with a switch_organization tool for users who belong to multiple orgs. Every tool response includes the active organization so the caller always knows which tenant is being acted on.",
  "homepage": "https://320px.com",
  "docs": "https://320px.com/llms-full.txt",
  "pricing": "https://320px.com/pricing",
  "contact": "hello@320px.com",
  "publisher": {
    "name": "Reach Out Labs GmbH",
    "url": "https://reachoutlabs.ch"
  },
  "mcp": {
    "endpoint": "https://api.320px.com/mcp",
    "transports": ["http", "sse"],
    "version": "1.1.0",
    "auth": {
      "type": "bearer",
      "description": "Per-user API key. Generate under Studio → Settings → API Keys. The key identifies both the default organization and the user — no organization_id parameter is ever required on tool calls."
    },
    "conventions": {
      "organization_scope": "implicit",
      "response_envelope": "Every successful tool response is text content prefixed with a `[organization] <name> (id: <id>)` header, followed by a JSON body `{ organization: { id, name, slug }, result: <payload> }`.",
      "active_org_ttl_seconds": 86400
    }
  },
  "tools": [
    {
      "name": "get_current_organization",
      "description": "Return the organization currently in use for this MCP session. Every other tool operates on this org. Call first to confirm which tenant the user is working with.",
      "inputs": {},
      "outputs": { "organization": "object" },
      "side_effects": "none",
      "idempotent": true
    },
    {
      "name": "list_organizations",
      "description": "List all organizations the authenticated user is a member of. Use to discover switch targets. Legacy API keys without user linkage return only the current organization.",
      "inputs": {},
      "outputs": { "organizations": "array" },
      "side_effects": "none",
      "idempotent": true
    },
    {
      "name": "switch_organization",
      "description": "Switch the active organization for this MCP session. Target must be one the authenticated user is a member of. Persists for 24 hours across MCP calls made with the same API key.",
      "inputs": {
        "organizationId": { "type": "string", "required": true, "description": "ID of the organization to switch to." }
      },
      "outputs": { "organization": "object" },
      "side_effects": "writes active-org override to session KV (24h TTL)",
      "idempotent": true
    },
    {
      "name": "list_contacts",
      "description": "List contacts in the active organization, sorted newest first.",
      "inputs": {
        "status": { "type": "string", "optional": true, "enum": ["ACTIVE", "UNSUBSCRIBED", "BOUNCED", "SUPPRESSED"] },
        "limit": { "type": "integer", "optional": true, "default": 25, "max": 100 }
      },
      "outputs": { "contacts": "array" },
      "side_effects": "none",
      "idempotent": true
    },
    {
      "name": "get_contact",
      "description": "Get a single contact by ID in the active organization.",
      "inputs": {
        "contactId": { "type": "string", "required": true }
      },
      "outputs": { "contact": "object" },
      "side_effects": "none",
      "idempotent": true
    },
    {
      "name": "create_contact",
      "description": "Create a new contact in the active organization.",
      "inputs": {
        "email": { "type": "string", "required": true },
        "firstName": { "type": "string", "optional": true },
        "lastName": { "type": "string", "optional": true },
        "company": { "type": "string", "optional": true }
      },
      "outputs": { "contact": "object" },
      "side_effects": "writes contact record",
      "idempotent": false
    },
    {
      "name": "list_campaigns",
      "description": "List campaigns in the active organization.",
      "inputs": {
        "status": { "type": "string", "optional": true, "enum": ["DRAFT", "SCHEDULED", "SENDING", "SENT", "PAUSED", "ARCHIVED"] },
        "limit": { "type": "integer", "optional": true, "default": 25, "max": 100 }
      },
      "outputs": { "campaigns": "array" },
      "side_effects": "none",
      "idempotent": true
    },
    {
      "name": "get_campaign_stats",
      "description": "Get delivery + engagement stats for a campaign in the active organization.",
      "inputs": {
        "campaignId": { "type": "string", "required": true }
      },
      "outputs": { "stats": "object" },
      "side_effects": "none",
      "idempotent": true
    },
    {
      "name": "list_segments",
      "description": "List segments in the active organization.",
      "inputs": {},
      "outputs": { "segments": "array" },
      "side_effects": "none",
      "idempotent": true
    },
    {
      "name": "get_analytics",
      "description": "Get analytics summaries (up to 30 rows) for the active organization.",
      "inputs": {
        "type": { "type": "string", "optional": true, "enum": ["daily", "weekly", "monthly"] }
      },
      "outputs": { "rows": "array" },
      "side_effects": "none",
      "idempotent": true
    },
    {
      "name": "send_campaign",
      "description": "Trigger sending a campaign in the active organization. The campaign must have a segment assigned.",
      "inputs": {
        "campaignId": { "type": "string", "required": true }
      },
      "outputs": { "job": "object" },
      "side_effects": "enqueues email send job; consumes email quota",
      "idempotent": false
    }
  ]
}
