{"openapi":"3.1.0","info":{"title":"CommentKeyword API","version":"1.0.0","description":"Public API for managing CommentKeyword keyword automations and reading AI agents. All operations use workspace-scoped Personal Access Tokens (PATs)."},"servers":[{"url":"https://commentkeyword.com/api","description":"Production"}],"tags":[{"name":"Agents","description":"Read AI agents that can be assigned to keyword automations."},{"name":"Keywords","description":"Create and manage Instagram comment keyword automations."}],"security":[{"bearerAuth":[]}],"paths":{"/agents":{"get":{"tags":["Agents"],"summary":"List AI agents","description":"Returns agents in the token workspace, newest first.","operationId":"listAgents","x-required-permissions":["agents:read or keywords:read"],"responses":{"200":{"description":"Agents returned successfully.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentListResponse"}}}},"401":{"description":"Authentication failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The token lacks agents:read and keywords:read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"The agents could not be fetched.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/keywords":{"get":{"tags":["Keywords"],"summary":"List keywords","operationId":"listKeywords","x-required-permissions":["keywords:read"],"parameters":[{"name":"search","in":"query","description":"Case-insensitive keyword search.","schema":{"type":"string"}},{"name":"status","in":"query","description":"Filter by activation state.","schema":{"type":"string","enum":["active","inactive"]}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"Keywords returned successfully.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeywordListResponse"}}}},"401":{"description":"Authentication failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The token lacks keywords:read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"The keywords could not be fetched.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Keywords"],"summary":"Create a keyword automation","operationId":"createKeyword","x-required-permissions":["keywords:write"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateKeywordRequest"},"example":{"keyword":"pricing","matchMode":"contains","scope":"global","commentReplies":["Sent it to you, {{namefirst}}!"],"dmMessages":["Hey {{namefirst}}, here are the pricing details."],"trackLinks":true,"delaySec":20,"aiAgentId":"a6be1900-8f4f-4ae6-8f4e-48f8b28f0f17"}}}},"responses":{"201":{"description":"Keyword created successfully.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeywordResponse"}}}},"400":{"description":"The request body is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Authentication failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The token lacks keywords:write.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The trigger conflicts with another automation or the workspace keyword limit has been reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"The keyword could not be created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/keywords/{id}":{"get":{"tags":["Keywords"],"summary":"Get a keyword","operationId":"getKeyword","x-required-permissions":["keywords:read"],"parameters":[{"name":"id","in":"path","required":true,"description":"The keyword public ID returned by the API.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Keyword returned successfully.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeywordResponse"}}}},"400":{"description":"The keyword ID is not a valid public UUID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Authentication failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The token lacks keywords:read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"The keyword was not found in the token workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"The keyword could not be fetched.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Keywords"],"summary":"Update a keyword","description":"Partially updates a keyword. Only provided fields are changed.","operationId":"updateKeyword","x-required-permissions":["keywords:write"],"parameters":[{"name":"id","in":"path","required":true,"description":"The keyword public ID returned by the API.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateKeywordRequest"}}}},"responses":{"200":{"description":"Keyword updated successfully.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeywordResponse"}}}},"400":{"description":"The keyword ID or request body is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Authentication failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The token lacks keywords:write.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"The keyword was not found in the token workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The updated trigger conflicts with another automation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"The keyword could not be updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Keywords"],"summary":"Delete a keyword","description":"Deletes a keyword. If it is linked to an AI agent, the first request returns DELETE_CONFIRMATION_REQUIRED; repeat with confirm=true after reviewing the effects.","operationId":"deleteKeyword","x-required-permissions":["keywords:delete"],"parameters":[{"name":"id","in":"path","required":true,"description":"The keyword public ID returned by the API.","schema":{"type":"string","format":"uuid"}},{"name":"confirm","in":"query","description":"Confirm deletion after a DELETE_CONFIRMATION_REQUIRED response.","schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"Keyword deleted successfully.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteKeywordResponse"}}}},"400":{"description":"The keyword ID is not a valid public UUID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Authentication failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The token lacks keywords:delete.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"The keyword was not found in the token workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Confirmation is required, or related records prevent safe deletion.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/DeleteConfirmationRequired"},{"$ref":"#/components/schemas/Error"}]}}}},"500":{"description":"The keyword could not be deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"PAT","description":"Workspace-scoped Personal Access Token created in CommentKeyword settings."}},"schemas":{"Error":{"type":"object","required":["error","message"],"properties":{"error":{"type":"string","example":"Bad Request"},"code":{"type":"string","description":"Stable machine-readable code when available."},"message":{"type":"string","example":"delaySec must be a number between 1 and 60"}}},"ResponseMeta":{"type":"object","required":["authType","workspaceId"],"properties":{"authType":{"type":"string","enum":["pat","session"]},"workspaceId":{"type":"integer"},"trackFirstKeywordCreated":{"type":"boolean"},"trackFirstAutomationActivated":{"type":"boolean"}}},"Agent":{"type":"object","required":["id","publicId","name","status","isActive"],"properties":{"id":{"type":"string","format":"uuid"},"publicId":{"type":"string","format":"uuid"},"name":{"type":"string"},"status":{"type":"string","enum":["live","inactive"]},"isActive":{"type":"boolean"}}},"Keyword":{"type":"object","required":["id","publicId","keyword","matchMode","matchWithoutAccents","scope","includeNextPost","nextPostRevision","mediaIds","autoAddedMediaIds","commentReplies","dmMessages","trackLinks","delaySec","aiAgentId","aiAgent","isActive","createdAt","updatedAt"],"properties":{"id":{"type":"string","format":"uuid"},"publicId":{"type":"string","format":"uuid"},"keyword":{"type":"string","maxLength":100},"matchMode":{"type":"string","enum":["contains","exact"]},"matchWithoutAccents":{"type":"boolean"},"scope":{"type":"string","enum":["global","post_specific"]},"includeNextPost":{"type":"boolean","description":"Whether this keyword is currently waiting to add the next publication. Automatically false after a post is caught."},"nextPostArmedAt":{"type":["string","null"],"format":"date-time"},"nextPostMediaId":{"type":["string","null"]},"nextPostClaimedAt":{"type":["string","null"],"format":"date-time"},"nextPostLastAttemptAt":{"type":["string","null"],"format":"date-time"},"nextPostLastError":{"type":["string","null"]},"nextPostRevision":{"type":"integer"},"nextPostStatus":{"type":["string","null"],"enum":["waiting","attached","disabled","reconnect_required","failed",null]},"mediaIds":{"type":"array","items":{"type":"string"}},"autoAddedMediaIds":{"type":"array","items":{"type":"string"},"description":"Subset of mediaIds that were added automatically while includeNextPost was enabled."},"commentReplies":{"type":"array","items":{"type":"string"}},"dmMessages":{"type":"array","items":{"type":"string"}},"trackLinks":{"type":"boolean"},"delaySec":{"type":"integer","minimum":1,"maximum":60},"aiAgentId":{"type":["string","null"],"format":"uuid"},"aiAgent":{"oneOf":[{"$ref":"#/components/schemas/Agent"},{"type":"null"}]},"isActive":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"CreateKeywordRequest":{"type":"object","required":["keyword","dmMessages"],"properties":{"keyword":{"type":"string","minLength":1,"maxLength":100,"description":"Trigger text. Leading/trailing whitespace is normalized.","example":"pricing"},"matchMode":{"type":"string","enum":["contains","exact"],"default":"contains"},"matchWithoutAccents":{"type":"boolean","default":false,"description":"May only be enabled when the keyword contains Latin diacritics. For example, “guía” will also match “guia”."},"scope":{"type":"string","enum":["global","post_specific"],"default":"global","description":"`post_specific` matches only assigned posts (see mediaIds and includeNextPost)."},"commentReplies":{"type":"array","items":{"type":"string"},"default":[],"description":"Empty variants are removed. Each rendered variant is limited to 500 characters. Supports {{handle}}, {{name}}, and {{namefirst}}."},"dmMessages":{"type":"array","minItems":1,"items":{"type":"string","minLength":1},"description":"At least one non-empty variant. Each rendered DM plus an assigned agent conversation hook is limited to 1,000 characters. Supports {{handle}}, {{name}}, and {{namefirst}}."},"trackLinks":{"type":"boolean","default":false,"description":"Replace eligible links in DM variants with tracked links."},"delaySec":{"type":"integer","minimum":1,"maximum":60,"default":30,"description":"Delay before sending the DM."},"aiAgentId":{"type":["string","null"],"format":"uuid","description":"Public ID of a Live agent in the same workspace, or null to detach the agent."},"mediaIds":{"type":"array","uniqueItems":true,"items":{"type":"string","minLength":1},"description":"Instagram media IDs to match on. Required when scope is post_specific, unless includeNextPost is true. Values are trimmed and deduplicated."},"mediaUrls":{"type":"object","additionalProperties":{"type":"string","format":"uri"},"description":"Optional map of media ID to Instagram permalink. Used to retain an openable reference for selected posts."},"includeNextPost":{"type":"boolean","default":false,"description":"post_specific only. Turns waiting for the next published post or reel on or off. Once found, the post is added to mediaIds and this value automatically becomes false. PATCH false stops a pending wait without removing assigned posts."}},"allOf":[{"if":{"properties":{"scope":{"const":"post_specific"}},"required":["scope"]},"then":{"anyOf":[{"required":["mediaIds"],"properties":{"mediaIds":{"minItems":1}}},{"required":["includeNextPost"],"properties":{"includeNextPost":{"const":true}}}]}}]},"UpdateKeywordRequest":{"type":"object","properties":{"keyword":{"type":"string","minLength":1,"maxLength":100,"description":"Trigger text. Leading/trailing whitespace is normalized.","example":"pricing"},"matchMode":{"type":"string","enum":["contains","exact"],"default":"contains"},"matchWithoutAccents":{"type":"boolean","default":false,"description":"May only be enabled when the keyword contains Latin diacritics. For example, “guía” will also match “guia”."},"scope":{"type":"string","enum":["global","post_specific"],"default":"global","description":"`post_specific` matches only assigned posts (see mediaIds and includeNextPost)."},"commentReplies":{"type":"array","items":{"type":"string"},"default":[],"description":"Empty variants are removed. Each rendered variant is limited to 500 characters. Supports {{handle}}, {{name}}, and {{namefirst}}."},"dmMessages":{"type":"array","minItems":1,"items":{"type":"string","minLength":1},"description":"At least one non-empty variant. Each rendered DM plus an assigned agent conversation hook is limited to 1,000 characters. Supports {{handle}}, {{name}}, and {{namefirst}}."},"trackLinks":{"type":"boolean","default":false,"description":"Replace eligible links in DM variants with tracked links."},"delaySec":{"type":"integer","minimum":1,"maximum":60,"default":30,"description":"Delay before sending the DM."},"aiAgentId":{"type":["string","null"],"format":"uuid","description":"Public ID of a Live agent in the same workspace, or null to detach the agent."},"mediaIds":{"type":"array","uniqueItems":true,"items":{"type":"string","minLength":1},"description":"Instagram media IDs to match on. Required when scope is post_specific, unless includeNextPost is true. Values are trimmed and deduplicated."},"mediaUrls":{"type":"object","additionalProperties":{"type":"string","format":"uri"},"description":"Optional map of media ID to Instagram permalink. Used to retain an openable reference for selected posts."},"includeNextPost":{"type":"boolean","default":false,"description":"post_specific only. Turns waiting for the next published post or reel on or off. Once found, the post is added to mediaIds and this value automatically becomes false. PATCH false stops a pending wait without removing assigned posts."},"isActive":{"type":"boolean"}}},"AgentListResponse":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Agent"}},"meta":{"$ref":"#/components/schemas/ResponseMeta"}}},"KeywordResponse":{"type":"object","required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/Keyword"},"meta":{"$ref":"#/components/schemas/ResponseMeta"}}},"KeywordListResponse":{"type":"object","required":["data","pagination","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Keyword"}},"pagination":{"type":"object","required":["total","limit","offset","hasMore"],"properties":{"total":{"type":"integer","minimum":0},"limit":{"type":"integer"},"offset":{"type":"integer"},"hasMore":{"type":"boolean"}}},"meta":{"$ref":"#/components/schemas/ResponseMeta"}}},"DeleteKeywordResponse":{"type":"object","required":["message","data","meta"],"properties":{"message":{"type":"string","const":"Keyword deleted successfully"},"data":{"type":"object","required":["id","publicId"],"properties":{"id":{"type":"string","format":"uuid"},"publicId":{"type":"string","format":"uuid"}}},"meta":{"$ref":"#/components/schemas/ResponseMeta"}}},"DeleteConfirmationRequired":{"type":"object","required":["error","code","message","details"],"properties":{"error":{"type":"string","const":"Conflict"},"code":{"type":"string","const":"DELETE_CONFIRMATION_REQUIRED"},"message":{"type":"string"},"details":{"type":"object","description":"Impact summary including linked agents, DM jobs, usage logs, conversations, and delete effects.","additionalProperties":true}}}}}}