{
  "openapi": "3.1.0",
  "info": {
    "title": "RemindMe API",
    "summary": "Public v1 API for RemindMe materials, reviews, search, and snoozing.",
    "version": "1.0.0",
    "description": "API keys are created in RemindMe Settings > Developer/API Access. Keys begin with rmk_live_, are shown only once, stored by RemindMe only as hashed values with metadata, and are revocable from the app UI. Use least-privilege scopes for agent and CLI integrations."
  },
  "externalDocs": {
    "description": "RemindMe public docs",
    "url": "https://docs.getremindme.app/docs/api"
  },
  "servers": [
    {
      "url": "https://api.getremindme.app/v1",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerApiKey": []
    },
    {
      "headerApiKey": []
    }
  ],
  "tags": [
    { "name": "Account", "description": "Authenticated account and API-key context." },
    { "name": "Materials", "description": "Create, list, and inspect study materials." },
    { "name": "Reviews", "description": "List due reviews, submit review quality scores, and snooze materials." },
    { "name": "Search", "description": "Search active materials by title, content, and source URL." }
  ],
  "paths": {
    "/me": {
      "get": {
        "tags": ["Account"],
        "summary": "Get authenticated account context",
        "operationId": "getMe",
        "externalDocs": { "url": "https://docs.getremindme.app/docs/api/authentication" },
        "responses": {
          "200": {
            "description": "Account context",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/MeResponse" },
                "examples": {
                  "me": {
                    "value": {
                      "id": "usr_123",
                      "email": "learner@example.com",
                      "firstName": "Learner",
                      "lastName": null,
                      "plan": "premium",
                      "apiKeyId": "key_123",
                      "scopes": ["materials:read", "reviews:read", "profile:read"]
                    }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/materials": {
      "get": {
        "tags": ["Materials"],
        "summary": "List active materials",
        "operationId": "listMaterials",
        "externalDocs": { "url": "https://docs.getremindme.app/docs/api/materials" },
        "parameters": [
          { "$ref": "#/components/parameters/Limit" },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": { "type": "string" },
            "description": "Optional search text matched against title, content, and source URL."
          }
        ],
        "responses": {
          "200": {
            "description": "Materials list",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/MaterialsResponse" },
                "examples": { "materials": { "$ref": "#/components/examples/MaterialsList" } }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/InsufficientScope" }
        },
        "x-remindme-scopes": ["materials:read"]
      },
      "post": {
        "tags": ["Materials"],
        "summary": "Create a material",
        "operationId": "createMaterial",
        "externalDocs": { "url": "https://docs.getremindme.app/docs/api/materials" },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/CreateMaterialRequest" },
              "examples": {
                "simple": {
                  "value": {
                    "title": "Read chapter 4",
                    "content": "Photosynthesis notes and active recall prompts",
                    "sourceUrl": "https://example.com/chapter-4",
                    "type": "simple"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Material created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["material"],
                  "properties": {
                    "material": { "$ref": "#/components/schemas/Material" }
                  }
                },
                "examples": { "created": { "$ref": "#/components/examples/MaterialCreated" } }
              }
            }
          },
          "400": { "$ref": "#/components/responses/InvalidRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/InsufficientScope" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        },
        "x-remindme-scopes": ["materials:write"]
      }
    },
    "/reviews/due": {
      "get": {
        "tags": ["Reviews"],
        "summary": "List due reviews",
        "operationId": "listDueReviews",
        "externalDocs": { "url": "https://docs.getremindme.app/docs/api/reviews" },
        "parameters": [
          { "$ref": "#/components/parameters/Limit" }
        ],
        "responses": {
          "200": {
            "description": "Due materials",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/MaterialsResponse" },
                "examples": { "due": { "$ref": "#/components/examples/MaterialsList" } }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/InsufficientScope" }
        },
        "x-remindme-scopes": ["reviews:read", "materials:read"]
      }
    },
    "/reviews/{materialId}": {
      "post": {
        "tags": ["Reviews"],
        "summary": "Submit a review quality score",
        "operationId": "submitReview",
        "externalDocs": { "url": "https://docs.getremindme.app/docs/api/reviews" },
        "parameters": [
          { "$ref": "#/components/parameters/MaterialId" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ReviewRequest" },
              "examples": {
                "quality4": { "value": { "quality": 4 } }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Review recorded",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ReviewResponse" },
                "examples": { "reviewed": { "$ref": "#/components/examples/ReviewRecorded" } }
              }
            }
          },
          "400": { "$ref": "#/components/responses/InvalidRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/InsufficientScope" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        },
        "x-remindme-scopes": ["reviews:write"]
      }
    },
    "/materials/{materialId}/snooze": {
      "post": {
        "tags": ["Reviews"],
        "summary": "Snooze one material",
        "operationId": "snoozeMaterial",
        "externalDocs": { "url": "https://docs.getremindme.app/docs/api/snooze" },
        "parameters": [
          { "$ref": "#/components/parameters/MaterialId" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/SnoozeRequest" },
              "examples": {
                "preset": { "value": { "preset": "tomorrow" } },
                "date": { "value": { "snoozeUntil": "2026-05-07T09:00:00.000Z", "reason": "agent-scheduled" } }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Snooze created",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/SnoozeResponse" },
                "examples": { "snoozed": { "$ref": "#/components/examples/SnoozeCreated" } }
              }
            }
          },
          "400": { "$ref": "#/components/responses/InvalidRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/InsufficientScope" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        },
        "x-remindme-scopes": ["snooze:write"]
      }
    },
    "/search": {
      "get": {
        "tags": ["Search"],
        "summary": "Search materials",
        "operationId": "searchMaterials",
        "externalDocs": { "url": "https://docs.getremindme.app/docs/api/search" },
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "schema": { "type": "string", "minLength": 1 }
          },
          { "$ref": "#/components/parameters/Limit" }
        ],
        "responses": {
          "200": {
            "description": "Matching materials",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/MaterialsResponse" },
                "examples": { "matches": { "$ref": "#/components/examples/MaterialsList" } }
              }
            }
          },
          "400": { "$ref": "#/components/responses/InvalidRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/InsufficientScope" }
        },
        "x-remindme-scopes": ["materials:read"]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerApiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "RemindMe API key, for example `Authorization: Bearer rmk_live_...`."
      },
      "headerApiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "X-RemindMe-API-Key",
        "description": "Alternative API-key header for clients that cannot send bearer auth."
      }
    },
    "parameters": {
      "Limit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 20
        }
      },
      "MaterialId": {
        "name": "materialId",
        "in": "path",
        "required": true,
        "schema": { "type": "string" }
      }
    },
    "schemas": {
      "Scope": {
        "type": "string",
        "enum": ["materials:read", "materials:write", "reviews:read", "reviews:write", "snooze:write", "profile:read"]
      },
      "MeResponse": {
        "type": "object",
        "required": ["id", "email", "plan", "apiKeyId", "scopes"],
        "properties": {
          "id": { "type": "string" },
          "email": { "type": "string", "format": "email" },
          "firstName": { "type": ["string", "null"] },
          "lastName": { "type": ["string", "null"] },
          "plan": { "type": "string" },
          "apiKeyId": { "type": "string" },
          "scopes": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/Scope" }
          }
        }
      },
      "Material": {
        "type": "object",
        "required": ["id", "title", "content", "sourceUrl", "type", "ef", "interval", "repetitions", "nextReview", "lastReviewed", "createdAt", "updatedAt"],
        "properties": {
          "id": { "type": "string" },
          "title": { "type": "string" },
          "content": { "type": ["string", "null"] },
          "sourceUrl": { "type": ["string", "null"], "format": "uri" },
          "type": { "type": "string", "enum": ["simple", "material", "flashcard", "card"] },
          "ef": { "type": "number" },
          "interval": { "type": "integer" },
          "repetitions": { "type": "integer" },
          "nextReview": { "type": ["string", "null"], "format": "date-time" },
          "lastReviewed": { "type": ["string", "null"], "format": "date-time" },
          "createdAt": { "type": "string", "format": "date-time" },
          "updatedAt": { "type": "string", "format": "date-time" }
        }
      },
      "MaterialsResponse": {
        "type": "object",
        "required": ["materials"],
        "properties": {
          "materials": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/Material" }
          }
        }
      },
      "CreateMaterialRequest": {
        "type": "object",
        "required": ["title"],
        "properties": {
          "title": { "type": "string", "minLength": 1, "maxLength": 240 },
          "content": { "type": "string" },
          "sourceUrl": { "type": "string", "format": "uri" },
          "type": { "type": "string", "enum": ["simple", "material", "flashcard", "card"], "default": "simple" }
        }
      },
      "ReviewRequest": {
        "type": "object",
        "required": ["quality"],
        "properties": {
          "quality": { "type": "integer", "minimum": 0, "maximum": 5 }
        }
      },
      "ReviewResponse": {
        "type": "object",
        "required": ["material", "review"],
        "properties": {
          "material": { "$ref": "#/components/schemas/Material" },
          "review": {
            "type": "object",
            "required": ["quality", "reviewedAt", "previousDueAt"],
            "properties": {
              "quality": { "type": "integer", "minimum": 0, "maximum": 5 },
              "reviewedAt": { "type": "string", "format": "date-time" },
              "previousDueAt": { "type": ["string", "null"], "format": "date-time" }
            }
          }
        }
      },
      "SnoozeRequest": {
        "type": "object",
        "properties": {
          "snoozeUntil": { "type": "string", "format": "date-time" },
          "preset": { "type": "string", "enum": ["1h", "tomorrow", "tonight"] },
          "reason": { "type": "string" }
        },
        "anyOf": [
          { "required": ["snoozeUntil"] },
          { "required": ["preset"] }
        ]
      },
      "SnoozeResponse": {
        "type": "object",
        "required": ["snooze"],
        "properties": {
          "snooze": {
            "type": "object",
            "required": ["id", "materialIds", "snoozeUntil", "reason", "createdAt"],
            "properties": {
              "id": { "type": "string" },
              "materialIds": {
                "type": "array",
                "items": { "type": "string" }
              },
              "snoozeUntil": { "type": "string", "format": "date-time" },
              "reason": { "type": "string" },
              "createdAt": { "type": "string", "format": "date-time" }
            }
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "required": ["code", "message"],
            "properties": {
              "code": {
                "type": "string",
                "enum": ["unauthorized", "insufficient_scope", "invalid_request", "invalid_scope", "rate_limited", "not_found"]
              },
              "message": { "type": "string" },
              "details": {}
            }
          }
        }
      }
    },
    "examples": {
      "MaterialsList": {
        "value": {
          "materials": [
            {
              "id": "mat_123",
              "title": "Photosynthesis active recall",
              "content": "Explain light-dependent reactions without notes.",
              "sourceUrl": "https://example.com/chapter-4",
              "type": "simple",
              "ef": 2.5,
              "interval": 1,
              "repetitions": 1,
              "nextReview": "2026-05-07T15:30:00.000Z",
              "lastReviewed": "2026-05-06T15:30:00.000Z",
              "createdAt": "2026-05-06T15:30:00.000Z",
              "updatedAt": "2026-05-06T15:31:00.000Z"
            }
          ]
        }
      },
      "MaterialCreated": {
        "value": {
          "material": {
            "id": "mat_123",
            "title": "Photosynthesis active recall",
            "content": "Explain light-dependent reactions without notes.",
            "sourceUrl": "https://example.com/chapter-4",
            "type": "simple",
            "ef": 2.5,
            "interval": 0,
            "repetitions": 0,
            "nextReview": "2026-05-06T15:30:00.000Z",
            "lastReviewed": null,
            "createdAt": "2026-05-06T15:30:00.000Z",
            "updatedAt": "2026-05-06T15:30:00.000Z"
          }
        }
      },
      "ReviewRecorded": {
        "value": {
          "material": {
            "id": "mat_123",
            "title": "Photosynthesis active recall",
            "interval": 6,
            "repetitions": 2,
            "nextReview": "2026-05-12T16:00:00.000Z",
            "lastReviewed": "2026-05-06T16:00:00.000Z"
          },
          "review": {
            "quality": 4,
            "reviewedAt": "2026-05-06T16:00:00.000Z",
            "previousDueAt": "2026-05-06T15:30:00.000Z"
          }
        }
      },
      "SnoozeCreated": {
        "value": {
          "snooze": {
            "id": "snz_123",
            "materialIds": ["mat_123"],
            "snoozeUntil": "2026-05-07T09:00:00.000Z",
            "reason": "travel day",
            "createdAt": "2026-05-06T16:00:00.000Z"
          }
        }
      },
      "Unauthorized": {
        "value": { "error": { "code": "unauthorized", "message": "Valid API key required." } }
      },
      "InsufficientScope": {
        "value": {
          "error": {
            "code": "insufficient_scope",
            "message": "This endpoint requires materials:write.",
            "details": { "requiredScopes": ["materials:write"] }
          }
        }
      },
      "InvalidRequest": {
        "value": {
          "error": {
            "code": "invalid_request",
            "message": "title is required.",
            "details": { "field": "title" }
          }
        }
      },
      "RateLimited": {
        "value": { "error": { "code": "rate_limited", "message": "Too many write requests. Please retry later." } }
      },
      "NotFound": {
        "value": { "error": { "code": "not_found", "message": "Material not found." } }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Valid API key required",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "examples": { "unauthorized": { "$ref": "#/components/examples/Unauthorized" } } } }
      },
      "InsufficientScope": {
        "description": "API key does not have a required scope",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "examples": { "insufficientScope": { "$ref": "#/components/examples/InsufficientScope" } } } }
      },
      "InvalidRequest": {
        "description": "Invalid request body or query",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "examples": { "invalidRequest": { "$ref": "#/components/examples/InvalidRequest" } } } }
      },
      "RateLimited": {
        "description": "Rate limit exceeded",
        "headers": {
          "Retry-After": {
            "description": "Seconds to wait before retrying when provided.",
            "schema": { "type": "integer" }
          }
        },
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorResponse" },
            "examples": {
              "rateLimited": { "$ref": "#/components/examples/RateLimited" }
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "examples": { "notFound": { "$ref": "#/components/examples/NotFound" } } } }
      }
    }
  }
}
