{
  "openapi": "3.1.0",
  "info": {
    "title": "Niyra API",
    "description": "Delegate natural-language tasks to the user's personal Niyra — a managed AI assistant with memory, integrations, and her own toolset. REST mirror of the Niyra MCP server.",
    "version": "1.0.0",
    "contact": {
      "name": "Niyra",
      "url": "https://niyra.ai/docs/api"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://api.niyra.ai",
      "description": "Production"
    }
  ],
  "security": [{"bearerAuth": []}],
  "paths": {
    "/v1/public/ask": {
      "post": {
        "operationId": "askNiyra",
        "summary": "Delegate a read-only question to Niyra",
        "description": "Run an agent loop using Niyra's read-only tools (web search, memory, calendar lookup, etc.). For actions that change state, use /v1/public/execute.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {"$ref": "#/components/schemas/AskRequest"}
            }
          }
        },
        "responses": {
          "200": {
            "description": "Agent response (synchronous) or task spawned (asynchronous)",
            "content": {
              "application/json": {
                "schema": {"$ref": "#/components/schemas/AskResponse"}
              }
            }
          },
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "403": {"$ref": "#/components/responses/Forbidden"},
          "429": {"$ref": "#/components/responses/RateLimited"}
        }
      }
    },
    "/v1/public/execute": {
      "post": {
        "operationId": "executeWithNiyra",
        "summary": "Ask Niyra to take an action",
        "description": "Run a write/action agent loop (send email, schedule meeting, etc.). Destructive actions still require user confirmation in their primary Niyra channel even with this scope granted.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {"$ref": "#/components/schemas/ExecuteRequest"}
            }
          }
        },
        "responses": {
          "200": {
            "description": "Action acknowledged or task spawned",
            "content": {
              "application/json": {
                "schema": {"$ref": "#/components/schemas/AskResponse"}
              }
            }
          },
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "403": {"$ref": "#/components/responses/Forbidden"},
          "429": {"$ref": "#/components/responses/RateLimited"}
        }
      }
    },
    "/v1/public/followup": {
      "post": {
        "operationId": "followupWithNiyra",
        "summary": "Continue an existing Niyra conversation",
        "description": "Pick up a prior thread with full memory + tool context. Preferred over starting a fresh ask when the user is replying to a previous Niyra turn.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {"$ref": "#/components/schemas/FollowupRequest"}
            }
          }
        },
        "responses": {
          "200": {
            "description": "Continuation response",
            "content": {
              "application/json": {
                "schema": {"$ref": "#/components/schemas/AskResponse"}
              }
            }
          },
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "403": {"$ref": "#/components/responses/Forbidden"},
          "429": {"$ref": "#/components/responses/RateLimited"}
        }
      }
    },
    "/v1/public/memories": {
      "post": {
        "operationId": "searchMemories",
        "summary": "Search Niyra's memory",
        "description": "Semantic + keyword search across the user's saved memories. Returns top-K matches.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {"$ref": "#/components/schemas/MemoriesRequest"}
            }
          }
        },
        "responses": {
          "200": {
            "description": "Search results",
            "content": {
              "application/json": {
                "schema": {"$ref": "#/components/schemas/MemoriesResponse"}
              }
            }
          },
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "403": {"$ref": "#/components/responses/Forbidden"},
          "429": {"$ref": "#/components/responses/RateLimited"}
        }
      }
    },
    "/v1/public/remember": {
      "post": {
        "operationId": "saveMemory",
        "summary": "Save a memory",
        "description": "Store a fact, preference, or note in the user's Niyra memory for future recall.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {"$ref": "#/components/schemas/RememberRequest"}
            }
          }
        },
        "responses": {
          "200": {
            "description": "Memory saved",
            "content": {
              "application/json": {
                "schema": {"$ref": "#/components/schemas/RememberResponse"}
              }
            }
          },
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "403": {"$ref": "#/components/responses/Forbidden"},
          "429": {"$ref": "#/components/responses/RateLimited"}
        }
      }
    },
    "/v1/public/tasks/{id}": {
      "get": {
        "operationId": "getTask",
        "summary": "Poll a long-running Niyra task",
        "description": "Check the status of a task spawned by a prior ask/execute/followup call that returned a task_id.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {"type": "string", "format": "uuid"},
            "description": "The task_id returned from a prior call"
          }
        ],
        "responses": {
          "200": {
            "description": "Current task state",
            "content": {
              "application/json": {
                "schema": {"$ref": "#/components/schemas/TaskResponse"}
              }
            }
          },
          "404": {"description": "No task with that id for this user"},
          "401": {"$ref": "#/components/responses/Unauthorized"}
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Niyra-issued JWT obtained via OAuth 2.1 authorization code + PKCE. Register clients at POST /oauth/register; discover endpoints via /.well-known/oauth-authorization-server."
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid access token",
        "content": {
          "application/json": {
            "schema": {"$ref": "#/components/schemas/Error"}
          }
        }
      },
      "Forbidden": {
        "description": "Token lacks the required scope",
        "content": {
          "application/json": {
            "schema": {"$ref": "#/components/schemas/Error"}
          }
        }
      },
      "RateLimited": {
        "description": "Per-token rate limit exceeded",
        "content": {
          "application/json": {
            "schema": {"$ref": "#/components/schemas/Error"}
          }
        }
      }
    },
    "schemas": {
      "AskRequest": {
        "type": "object",
        "required": ["prompt"],
        "properties": {
          "prompt": {
            "type": "string",
            "description": "The question or task in natural language",
            "minLength": 1,
            "maxLength": 8000
          },
          "conversation_id": {
            "type": "string",
            "description": "Optional — continue an existing thread"
          }
        }
      },
      "ExecuteRequest": {
        "type": "object",
        "required": ["prompt"],
        "properties": {
          "prompt": {
            "type": "string",
            "description": "The action in natural language",
            "minLength": 1,
            "maxLength": 8000
          },
          "conversation_id": {
            "type": "string",
            "description": "Optional — continue an existing thread"
          }
        }
      },
      "FollowupRequest": {
        "type": "object",
        "required": ["conversation_id", "prompt"],
        "properties": {
          "conversation_id": {
            "type": "string",
            "description": "The conversation_id from a prior call"
          },
          "prompt": {
            "type": "string",
            "description": "The user's reply or next message",
            "minLength": 1,
            "maxLength": 8000
          }
        }
      },
      "AskResponse": {
        "type": "object",
        "properties": {
          "answer": {
            "type": "string",
            "description": "Niyra's response in natural language"
          },
          "status": {
            "type": "string",
            "enum": ["running", "complete"],
            "description": "Present when the agent is still working in the background"
          },
          "task_id": {
            "type": "string",
            "format": "uuid",
            "description": "Present when status=running — poll /v1/public/tasks/{id}"
          },
          "error": {
            "type": "string",
            "description": "Populated on tool-execution failures"
          },
          "is_error": {
            "type": "boolean"
          }
        }
      },
      "MemoriesRequest": {
        "type": "object",
        "required": ["query"],
        "properties": {
          "query": {"type": "string", "minLength": 1, "maxLength": 1000},
          "limit": {"type": "integer", "minimum": 1, "maximum": 20, "default": 5}
        }
      },
      "MemoriesResponse": {
        "type": "object",
        "properties": {
          "query": {"type": "string"},
          "hits": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {"type": "string"},
                "content": {"type": "string"},
                "snippet": {"type": "string"},
                "saved_at": {"type": "string", "format": "date-time"}
              }
            }
          },
          "count": {"type": "integer"}
        }
      },
      "RememberRequest": {
        "type": "object",
        "required": ["content"],
        "properties": {
          "content": {"type": "string", "minLength": 1, "maxLength": 4000}
        }
      },
      "RememberResponse": {
        "type": "object",
        "properties": {
          "memory_id": {"type": "string"},
          "content": {"type": "string"}
        }
      },
      "TaskResponse": {
        "type": "object",
        "properties": {
          "task_id": {"type": "string", "format": "uuid"},
          "status": {
            "type": "string",
            "enum": ["pending", "running", "complete", "failed", "cancelled"]
          },
          "result": {"type": "string"},
          "error": {"type": "string"},
          "conversation_url": {"type": "string"},
          "updated_at": {"type": "string", "format": "date-time"}
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {"type": "string"},
          "error_description": {"type": "string"}
        }
      }
    }
  }
}