openapi: 3.1.0
info:
  title: Ordentus API — core (calendar, tasks, feeds)
  description: 'One Ordentus user''s calendar, tasks/goals/projects, feeds and reminders. Auth: a personal API token sent as a bearer token. CORE action; sharing, settings and hidden events live in /ordentus-openapi-extras.yaml. Full reference: https://ordent.us/llms-api.txt'
  version: 1.1.0
servers:
- url: https://ordent.us
paths:
  /api/auth/me:
    get:
      operationId: whoami
      summary: Who the token belongs to, and what it can do. CALL THIS FIRST.
      description: 'Returns an `auth` object with the token''s permissions. ALWAYS call before any write: if `auth.can_write` is false the token is READ-ONLY — skip create/update/delete/putStore and tell the user to issue a read/write token in Account Settings → API Access.

        '
      responses:
        '200':
          description: The current user record, including an `auth` object with `can_write` and `scopes`.
  /api/events:
    get:
      operationId: listEvents
      summary: List events overlapping a date range.
      description: Pass expand=true with both start and end to expand recurring events into individual occurrences. Without expand=true only the series master record is returned. Calendars the user marked hidden-from-assistant are excluded by default; pass include_hidden=true or a feed_id to see them.
      parameters:
      - name: start
        in: query
        required: false
        schema:
          type: string
        description: ISO start bound, e.g. 2026-06-01T00:00:00+10:00
      - name: end
        in: query
        required: false
        schema:
          type: string
        description: ISO end bound
      - name: expand
        in: query
        required: false
        schema:
          type: boolean
          default: false
        description: 'Set to true to expand recurring events into individual occurrences within [start, end]. Requires both start and end. Recommended: always pass expand=true when you need every occurrence (e.g. weekly lectures).'
      - name: feed_id
        in: query
        required: false
        schema:
          type: string
      - name: include_hidden
        in: query
        required: false
        schema:
          type: boolean
          default: false
        description: Set true to include calendars the user marked hidden-from-assistant. Default false excludes them from this default view.
      - name: limit
        in: query
        required: false
        schema:
          type: integer
      responses:
        '200':
          description: A list of events.
    post:
      operationId: createEvent
      summary: Create an event on the user's calendar.
      description: Requires a write-scoped token. Check whoami.auth.can_write first; a read-only token returns 403.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - summary
              - dtstart
              properties:
                summary:
                  type: string
                  description: Event title
                dtstart:
                  type: string
                  description: ISO start, e.g. 2026-06-10T14:00:00+10:00 (or 2026-06-10 for all-day)
                dtend:
                  type: string
                  description: ISO end (optional)
                all_day:
                  type: boolean
                  default: false
                location:
                  type: string
                description:
                  type: string
                timezone:
                  type: string
                  description: IANA tz, e.g. Australia/Brisbane
                calendar:
                  type: string
                  description: Target calendar by NAME (e.g. "Personal"). Use this when the user names a calendar. Unknown names return 400 listing valid ones. Omit to use the default manual calendar.
                feed_id:
                  type: string
                  description: Target calendar by id (alternative to calendar; from listFeeds). Optional.
      responses:
        '200':
          description: The created event.
  /api/events/trash:
    get:
      operationId: listDeletedEvents
      summary: List soft-deleted (trashed) events for the current user.
      description: Trashed events are recoverable for 30 days via restoreEvent before the auto-purge removes them permanently.
      responses:
        '200':
          description: A list of trashed events, newest-deleted first.
  /api/events/{id}:
    get:
      operationId: getEvent
      summary: Fetch a single event by id (use before editing/deleting to confirm it).
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: The event.
    patch:
      operationId: updateEvent
      summary: Update fields on an event.
      description: Requires a write-scoped token. Check whoami.auth.can_write first; a read-only token returns 403.
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                summary:
                  type: string
                dtstart:
                  type: string
                dtend:
                  type: string
                all_day:
                  type: boolean
                location:
                  type: string
                description:
                  type: string
                timezone:
                  type: string
                  description: IANA tz, e.g. Australia/Brisbane
                calendar:
                  type: string
                  description: Move the event to a calendar by NAME (e.g. "Personal"). Manual calendars only; unknown names return 400 listing valid ones.
                feed_id:
                  type: string
                  description: Move the event to a calendar by id (alternative to calendar). Manual calendars only.
      responses:
        '200':
          description: The updated event.
    delete:
      operationId: deleteEvent
      summary: Delete an event (soft-delete by default; moves to Trash).
      description: 'Requires a write-scoped token. Check whoami.auth.can_write first; a read-only token returns 403. Default is a soft-delete: the event moves to Trash and can be restored within 30 days. Pass ?permanent=true only for events already in Trash that the user wants gone immediately.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      - name: permanent
        in: query
        required: false
        schema:
          type: boolean
          default: false
        description: If true, hard-delete immediately (skips Trash). Only use on events already in Trash. If false (default), soft-delete to Trash.
      responses:
        '200':
          description: Deleted.
  /api/events/{id}/restore:
    post:
      operationId: restoreEvent
      summary: Restore a trashed event back to the live calendar.
      description: Requires a write-scoped token. Returns 409 if the event is not in Trash.
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: The restored event.
  /api/feeds:
    get:
      operationId: listFeeds
      summary: List the user's calendars (feeds), with their ids and colours.
      description: Each feed includes agent_visible — when false the user hid that calendar from your default listEvents view (you can still read it with feed_id or include_hidden=true).
      responses:
        '200':
          description: A list of feeds.
    post:
      operationId: createFeed
      summary: Subscribe to a new calendar (feed), or create a manual calendar.
      description: 'Requires a write-scoped token. For an iCal subscription use source_type "remote" and supply url_or_path. For a calendar you add events to yourself, use source_type "manual" (url_or_path not required).

        '
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - name
              properties:
                name:
                  type: string
                source_type:
                  type: string
                  enum:
                  - remote
                  - local
                  - manual
                  default: remote
                url_or_path:
                  type: string
                  description: iCal/ICS URL — required for remote/local feeds.
                color:
                  type: string
                  default: '#2563EB'
                  description: Hex colour.
                enabled:
                  type: boolean
                  default: true
                agent_visible:
                  type: boolean
                  default: true
                  description: When false, this calendar is hidden from your default listEvents view (still readable via feed_id or include_hidden=true).
                account_group:
                  type: string
                  default: Default
      responses:
        '200':
          description: The created feed.
  /api/store/{kind}:
    get:
      operationId: getStore
      summary: Read a personal-store blob (todos, goals, projects, objectives).
      parameters:
      - name: kind
        in: path
        required: true
        schema:
          type: string
          enum:
          - todos
          - goals
          - projects
          - objectives
      responses:
        '200':
          description: The JSON blob.
    put:
      operationId: putStore
      summary: Replace a personal-store blob with new JSON.
      description: 'Write-scoped token required (403 if read-only). PUT replaces the ENTIRE blob, so GET it first, merge your changes, then send the full result. Wrap the blob in a top-level "data" key: {"data": <blob>}. For single-item edits prefer createStoreItem/updateStoreItem.'
      parameters:
      - name: kind
        in: path
        required: true
        schema:
          type: string
          enum:
          - todos
          - goals
          - projects
          - objectives
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - data
              description: 'Wrap the blob in a ''data'' key. Example: {"data": {"today": [...], "week": [...]}}'
              properties:
                data:
                  $ref: '#/components/schemas/StoreBlob'
      responses:
        '200':
          description: Saved.
  /api/store/{kind}/items:
    post:
      operationId: createStoreItem
      summary: Add a single task/goal/project/objective without rewriting the whole blob.
      description: 'PREFERRED over putStore for adding one item — safe, never drops other items. The body IS the item object (not wrapped in "data"); an id is generated if omitted. For todos, pick the list with the `bucket` query param. Write-scoped token required.'
      parameters:
      - name: kind
        in: path
        required: true
        schema:
          type: string
          enum:
          - todos
          - goals
          - projects
          - objectives
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              description: The item object. Todos/objectives need a non-empty title; goals/projects need a name. For todos set bucket (today by default). Todos may also set priority.
              properties:
                title:
                  type: string
                  description: Task title (required for todos/objectives).
                name:
                  type: string
                  description: Name (goals/projects/objectives).
                bucket:
                  type: string
                  enum:
                  - today
                  - week
                  - later
                  - completed
                  - archived
                  description: 'todos only: which list to add to (you can put it here instead of the query param). Defaults to today.'
                priority:
                  type: string
                  description: 'todos: low, med, or high.'
                done:
                  type: boolean
                due_date:
                  type: string
                  description: ISO date, e.g. 2026-06-10 (todos).
                notes:
                  type: string
              additionalProperties: true
      responses:
        '201':
          description: The created item, plus the bucket it landed in (todos).
  /api/store/{kind}/items/{item_id}:
    patch:
      operationId: updateStoreItem
      summary: Merge fields into one item, preserving every field you don't send.
      description: 'PREFERRED over putStore for editing one item. Body = just the fields to change; everything else is kept. Identify todos/goals/projects by `id`, objectives by ordinal `n`. The id/n cannot be changed. Write-scoped token required.'
      parameters:
      - name: kind
        in: path
        required: true
        schema:
          type: string
          enum:
          - todos
          - goals
          - projects
          - objectives
      - name: item_id
        in: path
        required: true
        schema:
          type: string
        description: The item's id (or, for objectives, its ordinal n e.g. "01").
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              description: Only the fields to change; everything else on the item is preserved.
              properties:
                title:
                  type: string
                done:
                  type: boolean
                priority:
                  type: string
                  description: 'todos: low, med, or high.'
                notes:
                  type: string
                due_date:
                  type: string
                  description: ISO date (todos).
              additionalProperties: true
      responses:
        '200':
          description: The updated item.
        '404':
          description: No item matches that id.
    delete:
      operationId: deleteStoreItem
      summary: Remove one task/goal/project/objective.
      description: Requires a write-scoped token. Returns 404 if no item matches the id (so a no-op is distinguishable from a success).
      parameters:
      - name: kind
        in: path
        required: true
        schema:
          type: string
          enum:
          - todos
          - goals
          - projects
          - objectives
      - name: item_id
        in: path
        required: true
        schema:
          type: string
      responses:
        '204':
          description: Deleted.
        '404':
          description: No item matches that id.
  /api/store/todos/items/{item_id}/move:
    post:
      operationId: moveTodo
      summary: Move a task to a different bucket (today/week/later/completed/archived).
      description: The task is moved unchanged. Requires a write-scoped token.
      parameters:
      - name: item_id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - bucket
              properties:
                bucket:
                  type: string
                  enum:
                  - today
                  - week
                  - later
                  - completed
                  - archived
                  description: Destination bucket.
      responses:
        '200':
          description: The moved task.
  /api/store/todos/items/{item_id}/complete:
    post:
      operationId: completeTodo
      summary: Mark a task done and move it to the completed bucket.
      description: Sets done=true and stamps completed_at. Requires a write-scoped token.
      parameters:
      - name: item_id
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: The completed task.
  /api/feeds/{id}:
    get:
      operationId: getFeed
      summary: Fetch a single calendar (feed) by id.
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: The feed.
    patch:
      operationId: updateFeed
      summary: Update a calendar's name, colour, enabled, or assistant-visibility.
      description: Requires a write-scoped token. Set agent_visible=false to hide a calendar from your default listEvents view without un-syncing it.
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                color:
                  type: string
                  description: 'Hex colour, e.g. #2563EB'
                enabled:
                  type: boolean
                agent_visible:
                  type: boolean
                  description: False hides this calendar from your default listEvents view (keeps syncing; still readable via feed_id or include_hidden=true).
                account_group:
                  type: string
      responses:
        '200':
          description: The updated feed.
    delete:
      operationId: deleteFeed
      summary: Remove a calendar (feed) subscription.
      description: Requires a write-scoped token.
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Deleted.
  /api/reminders:
    get:
      operationId: listReminders
      summary: List the user's event reminders.
      responses:
        '200':
          description: A list of reminders.
    post:
      operationId: createReminder
      summary: Add a reminder to an event.
      description: Requires a write-scoped token.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - event_id
              properties:
                event_id:
                  type: string
                minutes_before:
                  type: integer
                  default: 15
                  description: Minutes before the event to remind (0–40320).
      responses:
        '200':
          description: The created reminder.
  /api/reminders/{id}:
    delete:
      operationId: deleteReminder
      summary: Remove a reminder.
      description: Requires a write-scoped token.
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Deleted.
components:
  schemas:
    StoreItem:
      type: object
      description: One item in a personal-store bucket (task, goal, etc.).
      properties:
        id:
          type: string
        title:
          type: string
        done:
          type: boolean
        priority:
          type: string
          description: 'todos: low, med, or high.'
        due_date:
          type: string
          description: ISO date, e.g. 2026-06-10 (optional).
        notes:
          type: string
      additionalProperties: true
    StoreBlob:
      type: object
      description: 'The full JSON blob for a store kind. For ''todos'' these buckets hold the items; other kinds (goals/projects/objectives) use the same shape.

        '
      properties:
        today:
          type: array
          items:
            $ref: '#/components/schemas/StoreItem'
        week:
          type: array
          items:
            $ref: '#/components/schemas/StoreItem'
        later:
          type: array
          items:
            $ref: '#/components/schemas/StoreItem'
        completed:
          type: array
          items:
            $ref: '#/components/schemas/StoreItem'
        archived:
          type: array
          items:
            $ref: '#/components/schemas/StoreItem'
      additionalProperties: true
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: 'Your personal API token (Account Settings → API Access), sent as Authorization: Bearer <token>.'
security:
- bearerAuth: []
