{
  "openapi": "3.1.0",
  "info": {
    "title": "Social Automation API",
    "version": "1.0.0",
    "summary": "Instagram, Facebook and Threads comment auto-reply and comment-to-DM automation.",
    "description": "Social Automation by Crescendo Lab replies to social comments automatically: when someone comments a keyword, it posts a public reply and sends them a DM (Instagram DM or Messenger), using Meta's official APIs.\n\n- Base URL: `https://social-automation.cresclab.com/api/v1`\n- Auth: `Authorization: Bearer sa_live_...` — create a key in the dashboard under API & AI (`/dashboard/api`). Keys are `read` or `read+write`.\n- Activities created through the API are drafts unless you pass `status: \"active\"`; activating makes the real account reply to real people.\n- Connecting Instagram/Facebook/Threads accounts requires a Meta login in the browser and is not available through the API.\n- Rate limit: 120 requests per minute per key (per server instance). Errors: `{ \"error\": { \"code\", \"message\", \"action_url\"? } }`.\n- MCP: the same operations are available as MCP tools at `https://social-automation.cresclab.com/api/mcp`. Guide: https://social-automation.cresclab.com/developers/mcp",
    "contact": {
      "name": "Crescendo Lab",
      "email": "info@cresclab.com",
      "url": "https://social-automation.cresclab.com/developers"
    },
    "termsOfService": "https://social-automation.cresclab.com/terms"
  },
  "servers": [
    {
      "url": "https://social-automation.cresclab.com/api/v1",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Workspace"
    },
    {
      "name": "Accounts",
      "description": "Connected Instagram accounts, Facebook Pages and Threads accounts."
    },
    {
      "name": "Activities",
      "description": "Comment automations: trigger + keywords → public reply and/or DM."
    },
    {
      "name": "Logs",
      "description": "Trigger history and daily stats."
    }
  ],
  "paths": {
    "/me": {
      "get": {
        "tags": [
          "Workspace"
        ],
        "operationId": "getWorkspace",
        "summary": "Workspace, plan and usage for this API key",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Workspace"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request (see error.field)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope, trial expired, contract expired or plan limit (see error.code and error.action_url)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/accounts": {
      "get": {
        "tags": [
          "Accounts"
        ],
        "operationId": "listAccounts",
        "summary": "List connected accounts",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "accounts": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Account"
                      }
                    },
                    "connect_url": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request (see error.field)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope, trial expired, contract expired or plan limit (see error.code and error.action_url)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/accounts/{id}/posts": {
      "get": {
        "tags": [
          "Accounts"
        ],
        "operationId": "listRecentPosts",
        "summary": "Recent posts of an account (live from Meta)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "kind",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "posts",
                "story"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "posts": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Post"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request (see error.field)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope, trial expired, contract expired or plan limit (see error.code and error.action_url)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Meta API error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/activities": {
      "get": {
        "tags": [
          "Activities"
        ],
        "operationId": "listActivities",
        "summary": "List activities with totals",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "active",
                "paused"
              ]
            }
          },
          {
            "name": "account_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "activities": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Activity"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request (see error.field)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope, trial expired, contract expired or plan limit (see error.code and error.action_url)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Activities"
        ],
        "operationId": "createActivity",
        "summary": "Create an activity (draft by default)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ActivityCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Activity"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request (see error.field)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope, trial expired, contract expired or plan limit (see error.code and error.action_url)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/activities/{id}": {
      "get": {
        "tags": [
          "Activities"
        ],
        "operationId": "getActivity",
        "summary": "Get an activity with a 14-day trend",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "activity": {
                      "$ref": "#/components/schemas/Activity"
                    },
                    "daily": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "date": {
                            "type": "string"
                          },
                          "successful_triggers": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request (see error.field)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope, trial expired, contract expired or plan limit (see error.code and error.action_url)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Activities"
        ],
        "operationId": "updateActivity",
        "summary": "Update an activity (partial); set status to activate or pause",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ActivityUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Activity"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request (see error.field)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope, trial expired, contract expired or plan limit (see error.code and error.action_url)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Activities"
        ],
        "operationId": "deleteActivity",
        "summary": "Delete an activity and its trigger logs",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "boolean"
                    },
                    "id": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request (see error.field)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope, trial expired, contract expired or plan limit (see error.code and error.action_url)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/logs": {
      "get": {
        "tags": [
          "Logs"
        ],
        "operationId": "listTriggerLogs",
        "summary": "Trigger logs, newest first",
        "parameters": [
          {
            "name": "activity_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "account_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "success",
                "failed"
              ]
            }
          },
          {
            "name": "days",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 365
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "logs": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TriggerLog"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "page": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request (see error.field)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope, trial expired, contract expired or plan limit (see error.code and error.action_url)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/stats": {
      "get": {
        "tags": [
          "Logs"
        ],
        "operationId": "getStats",
        "summary": "Daily public replies, DMs and failures",
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 90,
              "default": 7
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Stats"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request (see error.field)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope, trial expired, contract expired or plan limit (see error.code and error.action_url)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Workspace API key (sa_live_...)"
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "examples": [
                  "invalid_request",
                  "unauthorized",
                  "trial_expired",
                  "plan_limit",
                  "account_not_found",
                  "rate_limited"
                ]
              },
              "message": {
                "type": "string"
              },
              "field": {
                "type": "string"
              },
              "action_url": {
                "type": "string",
                "description": "Where the user can fix the problem (connect an account, upgrade, create a key)."
              },
              "retry_after": {
                "type": "integer"
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        }
      },
      "Trigger": {
        "type": "string",
        "enum": [
          "ig_comment",
          "ig_story",
          "ig_live",
          "fb_comment",
          "fb_live",
          "fb_message",
          "threads_reply"
        ],
        "description": "`ig_comment` (instagram): A comment on an Instagram post or Reel.\n`ig_story` (instagram): A reply to (or mention in) an Instagram Story. post_ids, if set, are story ids.\n`ig_live` (instagram): A comment during an Instagram Live broadcast (only while the broadcast is live).\n`fb_comment` (facebook): A comment on a Facebook Page post. The DM is sent through Messenger.\n`fb_live` (facebook): A comment during a Facebook Live broadcast (only while the broadcast is live).\n`fb_message` (facebook): A Messenger message sent to the Page; the activity replies in Messenger.\n`threads_reply` (threads): A reply to a Threads post. Public reply only: Meta offers no Threads DM API. Threads automation is in beta."
      },
      "DmBlock": {
        "type": "object",
        "description": "One block of the DM. A DM is ONE message in one of three shapes: (1) text blocks plus up to 3 link buttons, (2) exactly one image block on its own, or (3) a carousel of 1-10 card blocks on their own.",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "text",
              "button",
              "image",
              "card"
            ]
          },
          "text": {
            "type": "string",
            "description": "type=text: message text (all text together ≤1000 chars, ≤640 when buttons are present)."
          },
          "title": {
            "type": "string",
            "description": "type=button: button label ≤20 chars. type=card: card title ≤80 chars."
          },
          "url": {
            "type": "string",
            "description": "type=button: link opened by the button (full https URL)."
          },
          "image_url": {
            "type": "string",
            "description": "type=image or card: publicly reachable direct image URL (PNG/JPEG). Share-page links (Google Drive, Dropbox pages) are rejected."
          },
          "subtitle": {
            "type": "string",
            "description": "type=card: optional subtitle ≤80 chars."
          },
          "buttons": {
            "type": "array",
            "maxItems": 3,
            "description": "type=card: up to 3 link buttons on the card.",
            "items": {
              "type": "object",
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                }
              },
              "required": [
                "title",
                "url"
              ]
            }
          }
        },
        "required": [
          "type"
        ]
      },
      "ActivityCreate": {
        "type": "object",
        "properties": {
          "account_id": {
            "type": "string"
          },
          "trigger": {
            "$ref": "#/components/schemas/Trigger"
          },
          "name": {
            "type": "string",
            "maxLength": 100,
            "description": "Internal name shown in the dashboard."
          },
          "keywords": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 50,
            "description": "Keywords that trigger the activity, e.g. [\"+1\", \"want\", \"price\"]. Case-insensitive. Empty array = every comment triggers it."
          },
          "match": {
            "type": "string",
            "enum": [
              "contains",
              "exact"
            ],
            "description": "\"contains\" (default): comment contains a keyword. \"exact\": comment equals a keyword."
          },
          "public_replies": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 10,
            "description": "Public replies posted under the comment; one is picked at random each time (avoids looking like spam). Not available for ig_story and fb_message."
          },
          "dm": {
            "type": "array",
            "items": {
              "type": "object",
              "description": "One block of the DM. A DM is ONE message in one of three shapes: (1) text blocks plus up to 3 link buttons, (2) exactly one image block on its own, or (3) a carousel of 1-10 card blocks on their own.",
              "properties": {
                "type": {
                  "type": "string",
                  "enum": [
                    "text",
                    "button",
                    "image",
                    "card"
                  ]
                },
                "text": {
                  "type": "string",
                  "description": "type=text: message text (all text together ≤1000 chars, ≤640 when buttons are present)."
                },
                "title": {
                  "type": "string",
                  "description": "type=button: button label ≤20 chars. type=card: card title ≤80 chars."
                },
                "url": {
                  "type": "string",
                  "description": "type=button: link opened by the button (full https URL)."
                },
                "image_url": {
                  "type": "string",
                  "description": "type=image or card: publicly reachable direct image URL (PNG/JPEG). Share-page links (Google Drive, Dropbox pages) are rejected."
                },
                "subtitle": {
                  "type": "string",
                  "description": "type=card: optional subtitle ≤80 chars."
                },
                "buttons": {
                  "type": "array",
                  "maxItems": 3,
                  "description": "type=card: up to 3 link buttons on the card.",
                  "items": {
                    "type": "object",
                    "properties": {
                      "title": {
                        "type": "string"
                      },
                      "url": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "title",
                      "url"
                    ]
                  }
                }
              },
              "required": [
                "type"
              ]
            },
            "description": "The private message sent to the commenter (Instagram DM or Messenger). Not available for threads_reply."
          },
          "post_scope": {
            "type": "string",
            "enum": [
              "all",
              "selected",
              "next_post"
            ],
            "description": "\"all\" (default): every post. \"selected\": only post_ids. \"next_post\": the next Instagram post you publish (ig_comment only)."
          },
          "post_ids": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 50,
            "description": "Post ids from list_recent_posts, or full post URLs (Instagram/Threads URLs are resolved to ids, which needs a live Meta call)."
          },
          "follow_gate": {
            "type": "object",
            "description": "Instagram only (ig_comment, ig_live): ask the commenter to follow the account before the DM content is delivered.",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "opening_text": {
                "type": "string",
                "description": "First DM with a button, sent before checking the follow."
              },
              "button_title": {
                "type": "string",
                "description": "Label of that button, ≤20 chars."
              },
              "not_following_text": {
                "type": "string",
                "description": "Sent when the person does not follow yet."
              }
            },
            "required": [
              "enabled"
            ]
          },
          "start_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Optional start time, ISO 8601 with timezone (e.g. 2026-10-10T20:00:00+08:00)."
          },
          "end_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Optional end time, ISO 8601 with timezone. Must be after start_at."
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "active"
            ],
            "default": "draft"
          }
        },
        "required": [
          "account_id",
          "trigger"
        ]
      },
      "ActivityUpdate": {
        "type": "object",
        "properties": {
          "trigger": {
            "$ref": "#/components/schemas/Trigger"
          },
          "name": {
            "type": "string",
            "maxLength": 100,
            "description": "Internal name shown in the dashboard."
          },
          "keywords": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 50,
            "description": "Keywords that trigger the activity, e.g. [\"+1\", \"want\", \"price\"]. Case-insensitive. Empty array = every comment triggers it."
          },
          "match": {
            "type": "string",
            "enum": [
              "contains",
              "exact"
            ],
            "description": "\"contains\" (default): comment contains a keyword. \"exact\": comment equals a keyword."
          },
          "public_replies": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 10,
            "description": "Public replies posted under the comment; one is picked at random each time (avoids looking like spam). Not available for ig_story and fb_message."
          },
          "dm": {
            "type": "array",
            "items": {
              "type": "object",
              "description": "One block of the DM. A DM is ONE message in one of three shapes: (1) text blocks plus up to 3 link buttons, (2) exactly one image block on its own, or (3) a carousel of 1-10 card blocks on their own.",
              "properties": {
                "type": {
                  "type": "string",
                  "enum": [
                    "text",
                    "button",
                    "image",
                    "card"
                  ]
                },
                "text": {
                  "type": "string",
                  "description": "type=text: message text (all text together ≤1000 chars, ≤640 when buttons are present)."
                },
                "title": {
                  "type": "string",
                  "description": "type=button: button label ≤20 chars. type=card: card title ≤80 chars."
                },
                "url": {
                  "type": "string",
                  "description": "type=button: link opened by the button (full https URL)."
                },
                "image_url": {
                  "type": "string",
                  "description": "type=image or card: publicly reachable direct image URL (PNG/JPEG). Share-page links (Google Drive, Dropbox pages) are rejected."
                },
                "subtitle": {
                  "type": "string",
                  "description": "type=card: optional subtitle ≤80 chars."
                },
                "buttons": {
                  "type": "array",
                  "maxItems": 3,
                  "description": "type=card: up to 3 link buttons on the card.",
                  "items": {
                    "type": "object",
                    "properties": {
                      "title": {
                        "type": "string"
                      },
                      "url": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "title",
                      "url"
                    ]
                  }
                }
              },
              "required": [
                "type"
              ]
            },
            "description": "The private message sent to the commenter (Instagram DM or Messenger). Not available for threads_reply."
          },
          "post_scope": {
            "type": "string",
            "enum": [
              "all",
              "selected",
              "next_post"
            ],
            "description": "\"all\" (default): every post. \"selected\": only post_ids. \"next_post\": the next Instagram post you publish (ig_comment only)."
          },
          "post_ids": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 50,
            "description": "Post ids from list_recent_posts, or full post URLs (Instagram/Threads URLs are resolved to ids, which needs a live Meta call)."
          },
          "follow_gate": {
            "type": "object",
            "description": "Instagram only (ig_comment, ig_live): ask the commenter to follow the account before the DM content is delivered.",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "opening_text": {
                "type": "string",
                "description": "First DM with a button, sent before checking the follow."
              },
              "button_title": {
                "type": "string",
                "description": "Label of that button, ≤20 chars."
              },
              "not_following_text": {
                "type": "string",
                "description": "Sent when the person does not follow yet."
              }
            },
            "required": [
              "enabled"
            ]
          },
          "start_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Optional start time, ISO 8601 with timezone (e.g. 2026-10-10T20:00:00+08:00)."
          },
          "end_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Optional end time, ISO 8601 with timezone. Must be after start_at."
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "active",
              "paused"
            ]
          }
        }
      },
      "Activity": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "active",
              "paused"
            ]
          },
          "account_id": {
            "type": "string"
          },
          "platform": {
            "type": "string",
            "enum": [
              "instagram",
              "facebook",
              "threads"
            ]
          },
          "trigger": {
            "$ref": "#/components/schemas/Trigger"
          },
          "keywords": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "match": {
            "type": "string",
            "enum": [
              "contains",
              "exact"
            ]
          },
          "public_replies": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "dm": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DmBlock"
            }
          },
          "post_scope": {
            "type": "string",
            "enum": [
              "all",
              "selected",
              "next_post"
            ]
          },
          "post_ids": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            }
          },
          "follow_gate": {
            "type": "object",
            "description": "Instagram only (ig_comment, ig_live): ask the commenter to follow the account before the DM content is delivered.",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "opening_text": {
                "type": "string",
                "description": "First DM with a button, sent before checking the follow."
              },
              "button_title": {
                "type": "string",
                "description": "Label of that button, ≤20 chars."
              },
              "not_following_text": {
                "type": "string",
                "description": "Sent when the person does not follow yet."
              }
            },
            "required": [
              "enabled"
            ]
          },
          "start_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "end_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          },
          "dashboard_url": {
            "type": "string"
          },
          "stats": {
            "type": "object",
            "properties": {
              "public_replies_sent": {
                "type": "integer"
              },
              "dms_sent": {
                "type": "integer"
              }
            }
          }
        }
      },
      "Account": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "platform": {
            "type": "string",
            "enum": [
              "instagram",
              "facebook",
              "threads"
            ]
          },
          "handle": {
            "type": "string"
          },
          "username": {
            "type": [
              "string",
              "null"
            ]
          },
          "page_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "supported_triggers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Trigger"
            }
          },
          "token_expires_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "connected_at": {
            "type": "string"
          }
        }
      },
      "Post": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "caption": {
            "type": [
              "string",
              "null"
            ]
          },
          "media_type": {
            "type": [
              "string",
              "null"
            ]
          },
          "timestamp": {
            "type": [
              "string",
              "null"
            ]
          },
          "permalink": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "TriggerLog": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "activity_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "activity_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "account_id": {
            "type": "string"
          },
          "trigger": {
            "type": "string"
          },
          "username": {
            "type": [
              "string",
              "null"
            ]
          },
          "comment_text": {
            "type": [
              "string",
              "null"
            ]
          },
          "post_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "action": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "success",
              "failed"
            ]
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string"
          }
        }
      },
      "Stats": {
        "type": "object",
        "properties": {
          "days": {
            "type": "integer"
          },
          "daily": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "date": {
                  "type": "string"
                },
                "public_replies": {
                  "type": "integer"
                },
                "dms": {
                  "type": "integer"
                },
                "failed": {
                  "type": "integer"
                }
              }
            }
          },
          "totals": {
            "type": "object",
            "properties": {
              "public_replies": {
                "type": "integer"
              },
              "dms": {
                "type": "integer"
              },
              "failed": {
                "type": "integer"
              }
            }
          }
        }
      },
      "Workspace": {
        "type": "object",
        "properties": {
          "user": {
            "type": "object"
          },
          "workspace": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "plan": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "trial_ends_at": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "service_status": {
                "type": "string"
              },
              "billing_url": {
                "type": "string"
              }
            }
          },
          "usage": {
            "type": [
              "object",
              "null"
            ]
          },
          "api_key": {
            "type": "object"
          }
        }
      }
    }
  }
}