openapi: 3.1.1
info:
  title: microfeed API
  version: 1.0.1
  description: Create, read, update, and delete content in this microfeed
    instance. Send an API key using Bearer authentication.
  license:
    name: GNU Affero General Public License v3.0
    identifier: AGPL-3.0-only
servers:
  - url: /api/v1/
    description: This microfeed instance API
tags:
  - name: Feed
    description: Read the complete feed.
  - name: Items
    description: Create and manage feed items.
  - name: Channel
    description: Update the primary channel.
  - name: Media
    description: Prepare same-origin media uploads.
security:
  - bearerAuth: []
paths:
  /feed/:
    get:
      operationId: getFeed
      summary: Get the feed
      tags:
        - Feed
      parameters:
        - in: query
          name: limit
          schema:
            type: integer
            minimum: 1
            maximum: 300
        - in: query
          name: next_cursor
          schema:
            type: string
        - in: query
          name: order
          schema:
            type: string
            enum:
              - asc
              - desc
        - in: query
          name: prev_cursor
          schema:
            type: string
        - in: query
          name: sort
          schema:
            type: string
            enum:
              - created_at
              - updated_at
              - published_at
              - newest_first
              - oldest_first
      responses:
        "200":
          description: Successful response.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Feed"
        "401":
          description: The API key is missing or invalid.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /items/:
    post:
      operationId: createItem
      summary: Create an item
      tags:
        - Items
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ItemInput"
      responses:
        "201":
          description: Successful response.
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                required:
                  - id
                additionalProperties: false
        "400":
          description: The request body is invalid.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: The API key is missing or invalid.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /items/{itemId}/:
    get:
      operationId: getItem
      summary: Get an item
      tags:
        - Items
      parameters:
        - in: path
          name: itemId
          schema:
            type: string
            minLength: 1
            description: The microfeed item ID or an item-page slug ending in that ID.
            example: 0HGJLSML3P1
          required: true
          description: The microfeed item ID or an item-page slug ending in that ID.
      responses:
        "200":
          description: Successful response.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Feed"
        "401":
          description: The API key is missing or invalid.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: The item does not exist.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    put:
      operationId: updateItem
      summary: Update an item
      description: Only provided fields are changed. Attachments, GUIDs, dates, and
        omitted fields are preserved.
      tags:
        - Items
      parameters:
        - in: path
          name: itemId
          schema:
            type: string
            minLength: 1
            description: The microfeed item ID or an item-page slug ending in that ID.
            example: 0HGJLSML3P1
          required: true
          description: The microfeed item ID or an item-page slug ending in that ID.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ItemInput"
      responses:
        "200":
          description: Successful response.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Item"
        "400":
          description: The request body or item ID is invalid.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: The API key is missing or invalid.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: The item does not exist.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    delete:
      operationId: deleteItem
      summary: Delete an item
      tags:
        - Items
      parameters:
        - in: path
          name: itemId
          schema:
            type: string
            minLength: 1
            description: The microfeed item ID or an item-page slug ending in that ID.
            example: 0HGJLSML3P1
          required: true
          description: The microfeed item ID or an item-page slug ending in that ID.
      responses:
        "200":
          description: Successful response.
          content:
            application/json:
              schema:
                type: object
                properties: {}
                additionalProperties: false
        "400":
          description: The item ID is invalid.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: The API key is missing or invalid.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: The item does not exist.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /channels/{channelId}/:
    put:
      operationId: updatePrimaryChannel
      summary: Update the primary channel
      tags:
        - Channel
      parameters:
        - in: path
          name: channelId
          schema:
            type: string
            const: primary
            description: microfeed currently exposes one primary channel.
          required: true
          description: microfeed currently exposes one primary channel.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ChannelInput"
      responses:
        "200":
          description: Successful response.
          content:
            application/json:
              schema:
                type: object
                properties: {}
                additionalProperties: false
        "400":
          description: The request body or channel ID is invalid.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: The API key is missing or invalid.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /media_files/presigned_urls/:
    post:
      operationId: prepareMediaUpload
      summary: Prepare a media upload
      description: Creates a short-lived same-origin upload URL. PUT the raw file
        bytes to presigned_url, then save media_url on an item or channel.
      tags:
        - Media
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UploadRequest"
      responses:
        "201":
          description: Successful response.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UploadResponse"
        "400":
          description: The upload request is invalid.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: The API key is missing or invalid.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "503":
          description: Media storage is unavailable.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
components:
  schemas:
    ItemInput:
      type: object
      properties:
        _microfeed:
          type: object
          propertyNames:
            type: string
          additionalProperties: {}
        attachment:
          $ref: "#/components/schemas/Attachment"
        attachments:
          maxItems: 1
          type: array
          items:
            $ref: "#/components/schemas/Attachment"
        content_html:
          type: string
        date_published:
          type: string
          format: date-time
          pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
        date_published_ms:
          type: integer
          minimum: 0
          maximum: 9007199254740991
        guid:
          type: string
        id:
          type: string
        image:
          type: string
          format: uri
        status:
          anyOf:
            - type: string
              enum:
                - published
                - unlisted
                - unpublished
            - type: integer
              minimum: 1
              maximum: 4
          description: The item visibility. Names are preferred; numeric values are
            retained for compatibility.
          example: published
        title:
          type: string
        url:
          type: string
          format: uri
      additionalProperties: {}
    Attachment:
      type: object
      properties:
        category:
          type: string
          enum:
            - audio
            - video
            - document
            - image
            - external_url
        duration_in_seconds:
          type: number
          minimum: 0
        mime_type:
          type: string
        size_in_byte:
          deprecated: true
          description: Deprecated singular spelling. Use size_in_bytes.
          type: integer
          minimum: 0
          maximum: 9007199254740991
        size_in_bytes:
          type: integer
          minimum: 0
          maximum: 9007199254740991
        url:
          type: string
          format: uri
      required:
        - category
        - url
      additionalProperties: {}
    ChannelInput:
      type: object
      properties:
        _microfeed:
          type: object
          propertyNames:
            type: string
          additionalProperties: {}
        authors:
          type: array
          items:
            type: object
            properties:
              name:
                type: string
            required:
              - name
        description:
          type: string
        expired:
          type: boolean
        home_page_url:
          deprecated: true
          type: string
          format: uri
        homepage_url:
          type: string
          format: uri
        icon:
          type: string
          format: uri
        language:
          type: string
        title:
          type: string
      additionalProperties: {}
    UploadRequest:
      type: object
      properties:
        category:
          type: string
          enum:
            - image
            - audio
            - video
            - document
        full_local_file_path:
          type: string
          minLength: 1
          description: A filename or local path used to preserve the extension. The server
            never reads this path.
          example: /tmp/episode.mp3
        item_id:
          description: Required for audio, video, and document uploads.
          type: string
        size:
          description: Expected upload size in bytes.
          type: integer
          minimum: 0
          maximum: 9007199254740991
        type:
          description: Expected media type, such as audio/mpeg.
          type: string
      required:
        - category
        - full_local_file_path
    Feed:
      type: object
      properties:
        _microfeed:
          type: object
          propertyNames:
            type: string
          additionalProperties: {}
        description:
          type: string
        favicon:
          type: string
        feed_url:
          type: string
        home_page_url:
          type: string
        icon:
          type: string
        items:
          type: array
          items:
            $ref: "#/components/schemas/Item"
        language:
          type: string
        next_url:
          type: string
        title:
          type: string
        version:
          type: string
      required:
        - items
        - version
      additionalProperties: {}
    Item:
      type: object
      properties:
        _microfeed:
          type: object
          propertyNames:
            type: string
          additionalProperties: {}
        attachment:
          $ref: "#/components/schemas/Attachment"
        attachments:
          type: array
          items:
            $ref: "#/components/schemas/AttachmentOutput"
        content_html:
          type: string
        date_published:
          type: string
          format: date-time
          pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
        date_published_ms:
          type: integer
          minimum: 0
          maximum: 9007199254740991
        guid:
          type: string
        id:
          type: string
        image:
          type: string
        status:
          anyOf:
            - type: string
              enum:
                - published
                - unlisted
                - unpublished
            - type: integer
              minimum: 1
              maximum: 4
          description: The item visibility. Names are preferred; numeric values are
            retained for compatibility.
          example: published
        title:
          type: string
        url:
          type: string
        date_modified:
          type: string
          format: date-time
          pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
      required:
        - id
      additionalProperties: {}
    AttachmentOutput:
      type: object
      properties:
        category:
          type: string
          enum:
            - audio
            - video
            - document
            - image
            - external_url
        duration_in_seconds:
          type: number
          minimum: 0
        mime_type:
          type: string
        size_in_byte:
          deprecated: true
          description: Deprecated singular spelling. Use size_in_bytes.
          type: integer
          minimum: 0
          maximum: 9007199254740991
        size_in_bytes:
          type: integer
          minimum: 0
          maximum: 9007199254740991
        url:
          type: string
          minLength: 1
      required:
        - url
      additionalProperties: {}
    Error:
      type: object
      properties:
        error:
          type: string
      required:
        - error
      additionalProperties: false
    UploadResponse:
      type: object
      properties:
        media_url:
          type: string
          format: uri
          description: The URL to save in the item or channel after the upload succeeds.
        presigned_url:
          type: string
          format: uri
          description: Send the file bytes to this same-origin URL using HTTP PUT before
            saving media_url.
      required:
        - media_url
        - presigned_url
      additionalProperties: false
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: An API key sent using Bearer authentication.
