{
  "openapi": "3.0.3",
  "info": {
    "title": "keep.md API",
    "version": "0.1.0"
  },
  "servers": [
    {
      "url": "https://keep.md"
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer"
      }
    },
    "schemas": {
      "ItemContentItem": {
        "type": "object",
        "required": ["format", "markdown"],
        "properties": {
          "format": {
            "type": "string",
            "enum": ["markdown"]
          },
          "markdown": {
            "type": "string"
          },
          "meta": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "ItemContent": {
        "type": "object",
        "required": ["schemaVersion", "items"],
        "properties": {
          "schemaVersion": {
            "type": "integer",
            "enum": [2]
          },
          "items": {
            "type": "object",
            "additionalProperties": {
              "$ref": "#/components/schemas/ItemContentItem"
            }
          }
        }
      },
      "Item": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "urlHash": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "favicon": {
            "type": "string"
          },
          "createdAt": {
            "type": "integer"
          },
          "lastSeenAt": {
            "type": "integer"
          },
          "updatedAt": {
            "type": "integer"
          },
          "status": {
            "type": "string"
          },
          "timesAdded": {
            "type": "integer"
          },
          "notes": {
            "type": "string"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "contentExtractedAt": {
            "type": "integer"
          },
          "contentKey": {
            "type": "string"
          },
          "contentSize": {
            "type": "integer"
          },
          "contentExtractError": {
            "type": "string"
          },
          "contentTruncated": {
            "type": "boolean"
          },
          "contentSource": {
            "type": "string"
          },
          "contentAvailable": {
            "type": "boolean"
          },
          "contentMarkdown": {
            "type": "string"
          },
          "content": {
            "$ref": "#/components/schemas/ItemContent"
          },
          "processedAt": {
            "type": "integer",
            "description": "Timestamp when item was marked as processed (null if unprocessed)"
          }
        }
      },
      "ItemListResponse": {
        "type": "object",
        "required": ["items", "limit", "offset", "count"],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Item"
            }
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "count": {
            "type": "integer"
          }
        }
      },
      "ItemUpdateRequest": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string",
            "nullable": true
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "tags": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "string"
            }
          },
          "archived": {
            "type": "boolean"
          },
          "processed": {
            "type": "boolean"
          }
        }
      },
      "Source": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": ["rss", "youtube_channel", "x_articles", "email_inbox"]
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "pollEveryMin": {
            "type": "integer"
          },
          "nextPollAt": {
            "type": "integer",
            "nullable": true
          },
          "lastRunAt": {
            "type": "integer",
            "nullable": true
          },
          "lastSuccessAt": {
            "type": "integer",
            "nullable": true
          },
          "lastError": {
            "type": "string",
            "nullable": true
          },
          "inboxAddress": {
            "type": "string",
            "nullable": true
          },
          "config": {
            "type": "object",
            "additionalProperties": true
          },
          "createdAt": {
            "type": "integer"
          },
          "updatedAt": {
            "type": "integer"
          }
        },
        "additionalProperties": true
      },
      "SourceListResponse": {
        "type": "object",
        "required": ["sources"],
        "properties": {
          "sources": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Source"
            }
          }
        }
      },
      "SourceCreateRequest": {
        "type": "object",
        "required": ["type"],
        "properties": {
          "type": {
            "type": "string",
            "enum": ["rss", "youtube_channel", "x_articles", "email_inbox"]
          },
          "name": {
            "type": "string"
          },
          "pollEveryMin": {
            "type": "integer"
          },
          "feedUrl": {
            "type": "string"
          },
          "backfillMode": {
            "type": "string",
            "enum": ["from_now", "days", "articles"]
          },
          "channel": {
            "type": "string"
          },
          "channelId": {
            "type": "string"
          },
          "handle": {
            "type": "string"
          },
          "backfillDays": {
            "type": "integer"
          },
          "backfillItems": {
            "type": "integer"
          },
          "username": {
            "type": "string"
          },
          "input": {
            "type": "string"
          },
          "initialImportCount": {
            "type": "integer"
          }
        }
      },
      "SourceCreateResponse": {
        "type": "object",
        "properties": {
          "source": {
            "$ref": "#/components/schemas/Source"
          },
          "existing": {
            "type": "boolean"
          },
          "initialRun": {
            "type": "object",
            "additionalProperties": true
          }
        },
        "required": ["source"]
      },
      "OkResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          }
        },
        "required": ["ok"]
      },
      "SyncResponse": {
        "type": "object",
        "properties": {
          "received": {
            "type": "integer"
          },
          "accepted": {
            "type": "integer"
          },
          "upserted": {
            "type": "integer"
          },
          "added": {
            "type": "integer"
          },
          "skipped": {
            "type": "integer"
          },
          "limitReached": {
            "type": "boolean"
          },
          "linkLimit": {
            "type": "integer"
          },
          "linkCount": {
            "type": "integer"
          },
          "linkCountLifetime": {
            "type": "integer"
          },
          "linkCountPeriod": {
            "type": "integer"
          },
          "linkCountMonth": {
            "type": "integer"
          },
          "linkCountMonthKey": {
            "type": "string"
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "QuotaErrorResponse": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "enum": ["quota_reached"]
          },
          "message": {
            "type": "string"
          },
          "plan": {
            "type": "string"
          },
          "limitScope": {
            "type": "string",
            "enum": ["lifetime", "billing_cycle"]
          },
          "linkLimit": {
            "type": "integer"
          },
          "linkCount": {
            "type": "integer"
          },
          "sync": {
            "$ref": "#/components/schemas/SyncResponse"
          }
        },
        "required": ["error", "plan", "limitScope", "linkLimit", "linkCount"]
      },
      "IngestResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "extracted": {
            "type": "boolean"
          },
          "extractError": {
            "type": "string"
          },
          "background": {
            "type": "boolean"
          },
          "sync": {
            "$ref": "#/components/schemas/SyncResponse"
          }
        }
      }
    }
  },
  "paths": {
    "/api/health": {
      "get": {
        "summary": "Health check",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/me": {
      "get": {
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Get account info",
        "responses": {
          "200": {
            "description": "Account details"
          }
        }
      }
    },
    "/api/stats": {
      "get": {
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Get usage stats",
        "parameters": [
          {
            "name": "since",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "until",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Stats"
          }
        }
      }
    },
    "/api/items": {
      "get": {
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "List items (excludes archived by default)",
        "parameters": [
          {
            "name": "since",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "until",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated status filter. When omitted, archived items are excluded."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "include",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Include extra fields such as content. Use include=content."
          }
        ],
        "responses": {
          "200": {
            "description": "List of items",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ItemListResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/items/search": {
      "get": {
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Search items by keyword and semantic similarity",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Search query (title, URL, notes, tags). Uses hybrid retrieval."
          },
          {
            "name": "since",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "until",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated status filter. When omitted, archived items are excluded."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "include",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Include extra fields such as content. Use include=content."
          }
        ],
        "responses": {
          "200": {
            "description": "List of matching items",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ItemListResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/ingest": {
      "post": {
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Ingest a single URL (write-only mobile flow)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["url"],
                "properties": {
                  "url": {
                    "type": "string"
                  },
                  "source": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ingest result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IngestResponse"
                }
              }
            }
          },
          "429": {
            "description": "Plan quota reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuotaErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/items/delete": {
      "post": {
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Delete items",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Deleted"
          }
        }
      }
    },
    "/api/items/archive": {
      "post": {
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Archive items by ID",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Archived"
          }
        }
      }
    },
    "/api/feed": {
      "get": {
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "List unprocessed items with content (for agent consumption)",
        "parameters": [
          {
            "name": "since",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "until",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Search query (title, URL, notes, tags). Uses hybrid retrieval."
          }
        ],
        "responses": {
          "200": {
            "description": "List of unprocessed items with content included",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ItemListResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/items/mark-processed": {
      "post": {
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Mark items as processed",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Count of processed items",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "processed": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/items/{id}": {
      "get": {
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Get item metadata",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "include",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Include extra fields such as content. Use include=content."
          }
        ],
        "responses": {
          "200": {
            "description": "Item metadata",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Item"
                }
              }
            }
          }
        }
      },
      "post": {
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Update item metadata or state",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ItemUpdateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated item",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Item"
                }
              }
            }
          }
        }
      }
    },
    "/api/sources": {
      "get": {
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "List content sources and subscriptions",
        "responses": {
          "200": {
            "description": "List of sources",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SourceListResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Create a content source",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SourceCreateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created or existing source",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SourceCreateResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/sources/{id}/delete": {
      "post": {
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Delete a content source",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted source",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OkResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/items/{id}/content": {
      "get": {
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Get item content",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Markdown",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    }
  }
}
