openapi: 3.1.0
info:
  title: PostBoost API
  version: 1.0.0
  description: |
    The PostBoost REST API lets you publish, schedule, and analyze social media posts
    across 8+ platforms from a single integration. No OAuth apps to maintain —
    PostBoost handles platform authorization for you.

    ## Base URL
    All workspace-scoped endpoints are prefixed with `/{workspaceUuid}`.
    Panel/admin endpoints are prefixed with `/panel`.

    ## Authentication
    All requests require a Bearer token in the `Authorization` header.
    Generate tokens in your PostBoost dashboard under **Settings → Access Tokens**.

    ```
    Authorization: Bearer YOUR_API_TOKEN
    ```

  contact:
    name: PostBoost Support
    email: hi@postboost.co
    url: https://postboost.co
  license:
    name: Commercial
    url: https://postboost.co/pages/terms

servers:
  - url: https://postboost.co/app/api
    description: Production

security:
  - bearerAuth: []

tags:
  - name: Accounts
    description: Social media accounts connected to a workspace.
  - name: Media
    description: Upload and manage media assets in the workspace library.
  - name: Tags
    description: Organize posts with color-coded tags.
  - name: Posts
    description: Create, schedule, and publish posts across platforms.
  - name: Workspaces
    description: Manage workspaces (admin only).
  - name: Subscriptions
    description: Manage workspace subscriptions and billing (admin only).
  - name: Users
    description: Manage users across the platform (admin only).
  - name: Receipts
    description: Manage billing receipts (admin only).

paths:

  # ─── ACCOUNTS ────────────────────────────────────────────────────────────────

  /{workspaceUuid}/accounts:
    get:
      operationId: listAccounts
      tags: [Accounts]
      summary: List accounts
      description: Returns all social media accounts connected to the workspace.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
      responses:
        '200':
          description: List of accounts
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Account'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'

  /{workspaceUuid}/accounts/{accountUuid}:
    get:
      operationId: getAccount
      tags: [Accounts]
      summary: Get account
      description: Returns a single social media account.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
        - $ref: '#/components/parameters/accountUuid'
      responses:
        '200':
          description: Account details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Account'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'

  # ─── MEDIA ────────────────────────────────────────────────────────────────────

  /{workspaceUuid}/media:
    get:
      operationId: listMedia
      tags: [Media]
      summary: List media
      description: Returns a paginated list of media assets in the workspace library.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
        - name: page
          in: query
          schema:
            type: integer
            default: 1
          description: Page number (20 items per page).
      responses:
        '200':
          description: Paginated list of media
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginationMeta'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Media'
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      operationId: uploadMedia
      tags: [Media]
      summary: Upload media (binary)
      description: Upload a file directly as multipart form data.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file]
              properties:
                file:
                  type: string
                  format: binary
                  description: The file to upload.
                alt_text:
                  type: string
                  maxLength: 255
                  description: Alternative text for accessibility.
      responses:
        '201':
          description: Uploaded media object
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Media'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '422':
          $ref: '#/components/responses/ValidationError'
    delete:
      operationId: deleteMediaBulk
      tags: [Media]
      summary: Delete media (bulk)
      description: Delete one or more media items by their IDs.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [items]
              properties:
                items:
                  type: array
                  items:
                    type: integer
                  description: Array of media IDs to delete.
      responses:
        '200':
          $ref: '#/components/responses/SuccessResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /{workspaceUuid}/media/{mediaUuid}:
    get:
      operationId: getMedia
      tags: [Media]
      summary: Get media
      description: Returns a single media asset.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
        - $ref: '#/components/parameters/mediaUuid'
      responses:
        '200':
          description: Media asset
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Media'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      operationId: updateMedia
      tags: [Media]
      summary: Update media
      description: Update the alt text of a media asset.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
        - $ref: '#/components/parameters/mediaUuid'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [alt_text]
              properties:
                alt_text:
                  type: string
                  description: Alternative text for accessibility.
      responses:
        '200':
          $ref: '#/components/responses/SuccessResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /{workspaceUuid}/media/chunked/initiate:
    post:
      operationId: initiateChunkedUpload
      tags: [Media]
      summary: Initiate chunked upload
      description: |
        Start a chunked upload session for large files. Returns an `upload_uuid`,
        `chunk_size`, and `total_chunks` to use for subsequent chunk requests.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [filename, mime_type, total_size]
              properties:
                filename:
                  type: string
                  maxLength: 255
                mime_type:
                  type: string
                  example: video/mp4
                total_size:
                  type: integer
                  description: Total file size in bytes.
      responses:
        '201':
          description: Chunked upload session details
          content:
            application/json:
              schema:
                type: object
                properties:
                  upload_uuid:
                    type: string
                    format: uuid
                  chunk_size:
                    type: integer
                  total_chunks:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '422':
          $ref: '#/components/responses/ValidationError'

  /{workspaceUuid}/media/remote/initiate:
    post:
      operationId: initiateRemoteUpload
      tags: [Media]
      summary: Initiate remote upload
      description: |
        Download a file from a remote URL into the media library.
        For small files the media object is returned immediately.
        For large files a `download_id` is returned — poll the status endpoint.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [url]
              properties:
                url:
                  type: string
                  format: uri
                  description: URL of the remote file to download.
                alt_text:
                  type: string
                  maxLength: 255
      responses:
        '201':
          description: Media object or download job
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/Media'
                  - type: object
                    properties:
                      download_id:
                        type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '422':
          $ref: '#/components/responses/ValidationError'

  /{workspaceUuid}/media/chunked/{uploadUuid}/upload:
    post:
      operationId: uploadChunk
      tags: [Media]
      summary: Upload a chunk
      description: Upload a single chunk of a chunked upload session.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
        - name: uploadUuid
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [chunk, chunk_index]
              properties:
                chunk:
                  type: string
                  format: binary
                chunk_index:
                  type: integer
                  description: Zero-based index of this chunk.
      responses:
        '201':
          description: Chunk received
          content:
            application/json:
              schema:
                type: object
                properties:
                  received:
                    type: integer
                    description: Number of chunks received so far.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '422':
          $ref: '#/components/responses/ValidationError'

  /{workspaceUuid}/media/chunked/{uploadUuid}/complete:
    post:
      operationId: completeChunkedUpload
      tags: [Media]
      summary: Complete chunked upload
      description: Signal that all chunks have been uploaded. Returns the final media object.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
        - name: uploadUuid
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '201':
          description: Completed media object
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Media'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '422':
          $ref: '#/components/responses/ValidationError'

  /{workspaceUuid}/media/chunked/{uploadUuid}:
    delete:
      operationId: abortChunkedUpload
      tags: [Media]
      summary: Abort chunked upload
      description: Cancel an in-progress chunked upload session and clean up uploaded chunks.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
        - name: uploadUuid
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '204':
          description: Upload aborted
        '401':
          $ref: '#/components/responses/Unauthorized'

  /{workspaceUuid}/media/remote/{downloadId}/status:
    get:
      operationId: getRemoteUploadStatus
      tags: [Media]
      summary: Get remote upload status
      description: Poll the status of an in-progress remote media download.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
        - name: downloadId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Download status
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    enum: [pending, downloading, complete, failed]
                  media:
                    $ref: '#/components/schemas/Media'
                    description: Present when status is complete.
        '401':
          $ref: '#/components/responses/Unauthorized'

  # ─── TAGS ─────────────────────────────────────────────────────────────────────

  /{workspaceUuid}/tags:
    get:
      operationId: listTags
      tags: [Tags]
      summary: List tags
      description: Returns all tags defined in the workspace.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
      responses:
        '200':
          description: List of tags
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Tag'
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      operationId: createTag
      tags: [Tags]
      summary: Create tag
      description: Creates a new color-coded tag in the workspace for organizing posts.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TagInput'
      responses:
        '201':
          description: Created tag
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Tag'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '422':
          $ref: '#/components/responses/ValidationError'

  /{workspaceUuid}/tags/{tagUuid}:
    get:
      operationId: getTag
      tags: [Tags]
      summary: Get tag
      description: Returns a single tag by UUID.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
        - $ref: '#/components/parameters/tagUuid'
      responses:
        '200':
          description: Tag details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Tag'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      operationId: updateTag
      tags: [Tags]
      summary: Update tag
      description: Updates a tag's name or color.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
        - $ref: '#/components/parameters/tagUuid'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TagInput'
      responses:
        '200':
          $ref: '#/components/responses/SuccessResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/ValidationError'
    delete:
      operationId: deleteTag
      tags: [Tags]
      summary: Delete tag
      description: Permanently deletes a tag. Posts that had this tag attached are unaffected.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
        - $ref: '#/components/parameters/tagUuid'
      responses:
        '200':
          $ref: '#/components/responses/SuccessResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  # ─── POSTS ────────────────────────────────────────────────────────────────────

  /{workspaceUuid}/posts:
    get:
      operationId: listPosts
      tags: [Posts]
      summary: List posts
      description: Returns a paginated list of posts in the workspace.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
        - name: page
          in: query
          schema:
            type: integer
            default: 1
      responses:
        '200':
          description: Paginated list of posts
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginationMeta'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Post'
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      operationId: createPost
      tags: [Posts]
      summary: Create post
      description: |
        Create a new post. Use `schedule: true` with `date` and `time` to schedule,
        `schedule_now: true` to publish immediately, or `queue: true` to add to the
        smart publishing queue.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PostInput'
      responses:
        '201':
          description: Created post
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Post'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '422':
          $ref: '#/components/responses/ValidationError'
    delete:
      operationId: deletePostsBulk
      tags: [Posts]
      summary: Delete posts (bulk)
      description: Delete multiple posts at once.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [posts]
              properties:
                posts:
                  type: array
                  items:
                    type: string
                    format: uuid
                  description: Array of post UUIDs to delete.
                trash:
                  type: boolean
                  default: false
                  description: Move to trash instead of permanent delete.
                delete_mode:
                  $ref: '#/components/schemas/DeleteMode'
      responses:
        '200':
          description: Deletion result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeleteResult'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /{workspaceUuid}/posts/{postUuid}:
    get:
      operationId: getPost
      tags: [Posts]
      summary: Get post
      description: Returns a single post with all its versions and associated accounts.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
        - $ref: '#/components/parameters/postUuid'
      responses:
        '200':
          description: Post details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Post'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      operationId: updatePost
      tags: [Posts]
      summary: Update post
      description: Replaces a post's versions, accounts, tags, and scheduling options. The post must not be in a published state.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
        - $ref: '#/components/parameters/postUuid'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PostInput'
      responses:
        '200':
          $ref: '#/components/responses/SuccessResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/ValidationError'
    delete:
      operationId: deletePost
      tags: [Posts]
      summary: Delete post
      description: Deletes a post. Use `delete_mode` to control whether to also remove the published content from social platforms.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
        - $ref: '#/components/parameters/postUuid'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                trash:
                  type: boolean
                  default: false
                delete_mode:
                  $ref: '#/components/schemas/DeleteMode'
      responses:
        '200':
          description: Deletion result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeleteResult'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /{workspaceUuid}/posts/schedule/{postUuid}:
    post:
      operationId: schedulePost
      tags: [Posts]
      summary: Schedule post
      description: "Schedule an existing post. Set `postNow: true` to publish immediately."
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
        - $ref: '#/components/parameters/postUuid'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [postNow]
              properties:
                postNow:
                  type: boolean
      responses:
        '200':
          description: Schedule confirmation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScheduleResult'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /{workspaceUuid}/posts/add-to-queue/{postUuid}:
    post:
      operationId: addPostToQueue
      tags: [Posts]
      summary: Add post to queue
      description: Add an existing post to the smart publishing queue.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
        - $ref: '#/components/parameters/postUuid'
      responses:
        '201':
          description: Queue confirmation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScheduleResult'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /{workspaceUuid}/posts/approve/{postUuid}:
    post:
      operationId: approvePost
      tags: [Posts]
      summary: Approve post
      description: Approve a post that is pending review.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
        - $ref: '#/components/parameters/postUuid'
      responses:
        '200':
          description: Approval confirmation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScheduleResult'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'

  # ─── WORKSPACES (Panel/Admin) ─────────────────────────────────────────────────

  /panel/workspaces:
    get:
      operationId: listWorkspaces
      tags: [Workspaces]
      summary: List workspaces
      description: Returns a paginated list of all workspaces. Admin only.
      parameters:
        - name: keyword
          in: query
          schema:
            type: string
        - name: subscription_status
          in: query
          schema:
            type: string
            enum: [active, trialing, past_due, paused, canceled, incomplete]
        - name: access_status
          in: query
          schema:
            type: string
            enum: [subscription, unlimited, locked]
        - name: page
          in: query
          schema:
            type: integer
            default: 1
          description: Page number (15 items per page).
      responses:
        '200':
          description: Paginated list of workspaces
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginationMeta'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Workspace'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
    post:
      operationId: createWorkspace
      tags: [Workspaces]
      summary: Create workspace
      description: Create a new workspace. Admin only.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WorkspaceInput'
      responses:
        '201':
          description: Created workspace
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Workspace'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/ValidationError'
    delete:
      operationId: deleteWorkspacesBulk
      tags: [Workspaces]
      summary: Delete workspaces (bulk)
      description: Permanently deletes one or more workspaces and all their associated data. Admin only.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [workspaces]
              properties:
                workspaces:
                  type: array
                  items:
                    type: string
                    format: uuid
      responses:
        '200':
          $ref: '#/components/responses/SuccessResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'

  /panel/workspaces/{workspaceUuid}:
    get:
      operationId: getWorkspace
      tags: [Workspaces]
      summary: Get workspace
      description: Returns a single workspace by UUID including its subscription status. Admin only.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
      responses:
        '200':
          description: Workspace details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Workspace'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      operationId: updateWorkspace
      tags: [Workspaces]
      summary: Update workspace
      description: Updates a workspace's name, color, or access status. Admin only.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WorkspaceInput'
      responses:
        '200':
          $ref: '#/components/responses/SuccessResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      operationId: deleteWorkspace
      tags: [Workspaces]
      summary: Delete workspace
      description: Permanently deletes a single workspace and all its associated data. Admin only.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
      responses:
        '200':
          $ref: '#/components/responses/SuccessResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'

  /panel/workspaces/{workspaceUuid}/users:
    post:
      operationId: addUserToWorkspace
      tags: [Workspaces]
      summary: Add user to workspace
      description: Adds an existing user to the workspace with a specified role. Admin only.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WorkspaceUserInput'
      responses:
        '201':
          $ref: '#/components/responses/SuccessResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/ValidationError'
    put:
      operationId: updateWorkspaceUser
      tags: [Workspaces]
      summary: Update user role in workspace
      description: Changes a user's role or permissions within the workspace. Admin only.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WorkspaceUserInput'
      responses:
        '200':
          $ref: '#/components/responses/SuccessResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
    delete:
      operationId: removeUserFromWorkspace
      tags: [Workspaces]
      summary: Remove user from workspace
      description: Removes a user's access to the workspace. The user account is not deleted. Admin only.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [user_id]
              properties:
                user_id:
                  type: integer
      responses:
        '200':
          $ref: '#/components/responses/SuccessResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'

  # ─── SUBSCRIPTIONS (Panel/Admin) ──────────────────────────────────────────────

  /panel/workspaces/{workspaceUuid}/subscription:
    get:
      operationId: getSubscription
      tags: [Subscriptions]
      summary: Get subscription
      description: Returns the active subscription for the workspace, or `null` if none exists. Admin only.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
      responses:
        '200':
          description: Subscription details or null
          content:
            application/json:
              schema:
                nullable: true
                oneOf:
                  - $ref: '#/components/schemas/Subscription'
                  - type: 'null'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
    post:
      operationId: createSubscription
      tags: [Subscriptions]
      summary: Create subscription
      description: Manually creates a subscription record for the workspace (for external billing integrations). Admin only.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SubscriptionInput'
      responses:
        '201':
          $ref: '#/components/responses/SuccessResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/ValidationError'
    put:
      operationId: updateSubscription
      tags: [Subscriptions]
      summary: Update subscription
      description: Updates the plan ID, status, or trial/pause dates of an existing subscription. Admin only.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SubscriptionUpdateInput'
      responses:
        '200':
          $ref: '#/components/responses/SuccessResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
    delete:
      operationId: deleteSubscription
      tags: [Subscriptions]
      summary: Delete subscription
      description: Removes the subscription record from the workspace. Admin only.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
      responses:
        '200':
          $ref: '#/components/responses/SuccessResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'

  /panel/workspaces/{workspaceUuid}/subscription/new:
    post:
      operationId: checkoutSubscription
      tags: [Subscriptions]
      summary: New subscription checkout
      description: Returns a Stripe Checkout URL for a new subscription.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [plan_id, cycle]
              properties:
                plan_id:
                  type: integer
                cycle:
                  type: string
                  enum: [monthly, yearly]
                coupon:
                  type: string
      responses:
        '200':
          description: Checkout URL
          content:
            application/json:
              schema:
                type: object
                properties:
                  url:
                    type: string
                    format: uri
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'

  /panel/workspaces/{workspaceUuid}/subscription/change-plan:
    put:
      operationId: changeSubscriptionPlan
      tags: [Subscriptions]
      summary: Change subscription plan
      description: Switches the workspace to a different Stripe plan. Optionally prorates the change and bills immediately. Admin only.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [plan_id, cycle, prorate, billing_immediately]
              properties:
                plan_id:
                  type: integer
                cycle:
                  type: string
                  enum: [monthly, yearly]
                prorate:
                  type: boolean
                billing_immediately:
                  type: boolean
      responses:
        '200':
          $ref: '#/components/responses/SuccessResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'

  /panel/workspaces/{workspaceUuid}/subscription/cancel:
    post:
      operationId: cancelSubscription
      tags: [Subscriptions]
      summary: Cancel subscription
      description: Cancels the workspace's Stripe subscription at the end of the current billing period. Admin only.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
      responses:
        '200':
          $ref: '#/components/responses/SuccessResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'

  /panel/workspaces/{workspaceUuid}/subscription/resume:
    post:
      operationId: resumeSubscription
      tags: [Subscriptions]
      summary: Resume subscription
      description: Resumes a previously canceled subscription before it expires. Admin only.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
      responses:
        '200':
          $ref: '#/components/responses/SuccessResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'

  /panel/workspaces/{workspaceUuid}/subscription/generic:
    post:
      operationId: addGenericSubscription
      tags: [Subscriptions]
      summary: Add generic subscription
      description: Assigns a non-Stripe (generic) subscription plan to the workspace, optionally granting a trial period. Used for AppSumo-style lifetime deals. Admin only.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [plan_id, keep_prev_trial_ends_at]
              properties:
                plan_id:
                  type: integer
                trial_days:
                  type: integer
                  minimum: 0
                  maximum: 365
                keep_prev_trial_ends_at:
                  type: boolean
      responses:
        '201':
          $ref: '#/components/responses/SuccessResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
    delete:
      operationId: removeGenericSubscription
      tags: [Subscriptions]
      summary: Remove generic subscription
      description: Removes the generic (non-Stripe) subscription from the workspace. Admin only.
      parameters:
        - $ref: '#/components/parameters/workspaceUuid'
      responses:
        '200':
          $ref: '#/components/responses/SuccessResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'

  # ─── USERS (Panel/Admin) ──────────────────────────────────────────────────────

  /panel/users:
    get:
      operationId: listUsers
      tags: [Users]
      summary: List users
      description: Returns a paginated list of all users on the platform. Optionally filter by name or email. Admin only.
      parameters:
        - name: keyword
          in: query
          schema:
            type: string
          description: Search by name or email.
        - name: page
          in: query
          schema:
            type: integer
            default: 1
          description: Page number (15 items per page).
      responses:
        '200':
          description: Paginated list of users
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginationMeta'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/User'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
    post:
      operationId: createUser
      tags: [Users]
      summary: Create user
      description: Creates a new user account on the platform. Admin only.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UserInput'
      responses:
        '201':
          description: Created user
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/ValidationError'
    delete:
      operationId: deleteUsersBulk
      tags: [Users]
      summary: Delete users (bulk)
      description: Permanently deletes one or more user accounts. You cannot delete your own account. Admin only.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [users]
              properties:
                users:
                  type: array
                  items:
                    type: integer
      responses:
        '200':
          $ref: '#/components/responses/SuccessResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'

  /panel/users/{userId}:
    get:
      operationId: getUser
      tags: [Users]
      summary: Get user
      description: Returns a single user account by ID. Admin only.
      parameters:
        - $ref: '#/components/parameters/userId'
      responses:
        '200':
          description: User details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      operationId: updateUser
      tags: [Users]
      summary: Update user
      description: Updates a user's name, email, admin status, or password. Admin only.
      parameters:
        - $ref: '#/components/parameters/userId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UserUpdateInput'
      responses:
        '200':
          $ref: '#/components/responses/SuccessResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/ValidationError'
    delete:
      operationId: deleteUser
      tags: [Users]
      summary: Delete user
      description: Permanently deletes a user account. Returns 400 if you attempt to delete your own account. Admin only.
      parameters:
        - $ref: '#/components/parameters/userId'
      responses:
        '200':
          $ref: '#/components/responses/SuccessResponse'
        '400':
          description: Cannot delete own account
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: false
                  message:
                    type: string
                    example: You cannot delete yourself.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'

  # ─── RECEIPTS (Panel/Admin) ───────────────────────────────────────────────────

  /panel/receipts:
    get:
      operationId: listReceipts
      tags: [Receipts]
      summary: List receipts
      description: Returns a paginated list of billing receipts. Filter by workspace UUID or invoice number. Admin only.
      parameters:
        - name: workspace_uuid
          in: query
          schema:
            type: string
            format: uuid
        - name: invoice_number
          in: query
          schema:
            type: string
        - name: page
          in: query
          schema:
            type: integer
            default: 1
          description: Page number (15 items per page).
      responses:
        '200':
          description: Paginated list of receipts
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginationMeta'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Receipt'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
    post:
      operationId: createReceipt
      tags: [Receipts]
      summary: Create receipt
      description: Creates a billing receipt record for a workspace. Admin only.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReceiptInput'
      responses:
        '201':
          description: Created receipt
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Receipt'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/ValidationError'
    delete:
      operationId: deleteReceiptsBulk
      tags: [Receipts]
      summary: Delete receipts (bulk)
      description: Permanently deletes one or more receipt records. Admin only.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [receipts]
              properties:
                receipts:
                  type: array
                  items:
                    type: string
                    format: uuid
      responses:
        '200':
          $ref: '#/components/responses/SuccessResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'

  /panel/receipts/{receiptUuid}:
    get:
      operationId: getReceipt
      tags: [Receipts]
      summary: Get receipt
      description: Returns a single receipt by UUID. Admin only.
      parameters:
        - $ref: '#/components/parameters/receiptUuid'
      responses:
        '200':
          description: Receipt details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Receipt'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      operationId: updateReceipt
      tags: [Receipts]
      summary: Update receipt
      description: Updates a receipt's transaction details, amount, or payment date. Admin only.
      parameters:
        - $ref: '#/components/parameters/receiptUuid'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReceiptUpdateInput'
      responses:
        '200':
          $ref: '#/components/responses/SuccessResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      operationId: deleteReceipt
      tags: [Receipts]
      summary: Delete receipt
      description: Permanently deletes a single receipt. Admin only.
      parameters:
        - $ref: '#/components/parameters/receiptUuid'
      responses:
        '200':
          $ref: '#/components/responses/SuccessResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'

# ─── COMPONENTS ───────────────────────────────────────────────────────────────

components:

  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: |
        Generate API tokens in your PostBoost dashboard under **Settings → Access Tokens**.
        Include the token in every request:
        ```
        Authorization: Bearer YOUR_API_TOKEN
        ```

  parameters:
    workspaceUuid:
      name: workspaceUuid
      in: path
      required: true
      schema:
        type: string
        format: uuid
      description: UUID of the workspace.
    accountUuid:
      name: accountUuid
      in: path
      required: true
      schema:
        type: string
        format: uuid
      description: UUID of the social media account.
    mediaUuid:
      name: mediaUuid
      in: path
      required: true
      schema:
        type: string
        format: uuid
      description: UUID of the media asset.
    tagUuid:
      name: tagUuid
      in: path
      required: true
      schema:
        type: string
        format: uuid
      description: UUID of the tag.
    postUuid:
      name: postUuid
      in: path
      required: true
      schema:
        type: string
        format: uuid
      description: UUID of the post.
    userId:
      name: userId
      in: path
      required: true
      schema:
        type: integer
      description: ID of the user.
    receiptUuid:
      name: receiptUuid
      in: path
      required: true
      schema:
        type: string
        format: uuid
      description: UUID of the receipt.

  responses:
    Unauthorized:
      description: Missing or invalid API token.
      content:
        application/json:
          schema:
            type: object
            properties:
              message:
                type: string
                example: Unauthenticated.
    Forbidden:
      description: Insufficient permissions.
      content:
        application/json:
          schema:
            type: object
            properties:
              message:
                type: string
                example: Access forbidden.
    NotFound:
      description: Resource not found.
      content:
        application/json:
          schema:
            type: object
            properties:
              message:
                type: string
                example: Not found.
    ValidationError:
      description: Validation failed.
      content:
        application/json:
          schema:
            type: object
            properties:
              message:
                type: string
                example: Validation errors.
              errors:
                type: object
                additionalProperties:
                  type: array
                  items:
                    type: string
    SuccessResponse:
      description: Operation succeeded.
      content:
        application/json:
          schema:
            type: object
            properties:
              success:
                type: boolean
                example: true

  schemas:

    PaginationMeta:
      type: object
      properties:
        links:
          type: object
          properties:
            first:
              type: string
            last:
              type: string
            prev:
              type: string
              nullable: true
            next:
              type: string
              nullable: true
        meta:
          type: object
          properties:
            current_page:
              type: integer
            from:
              type: integer
            last_page:
              type: integer
            per_page:
              type: integer
            to:
              type: integer
            total:
              type: integer

    Account:
      type: object
      required: [id, uuid, name, username, provider, data, authorized, created_at]
      properties:
        id:
          type: integer
        uuid:
          type: string
          format: uuid
        name:
          type: string
          example: My Instagram
        username:
          type: string
          example: myhandle
        image:
          type: string
          format: uri
          nullable: true
        provider:
          type: string
          enum: [instagram, tiktok, pinterest, twitter, linkedin, facebook_page, youtube, mastodon]
        data:
          type: object
          description: Provider-specific metadata.
        authorized:
          type: boolean
        created_at:
          type: string
          format: date-time

    Media:
      type: object
      required: [id, uuid, name, mime_type, type, url, is_video, created_at]
      properties:
        id:
          type: integer
        uuid:
          type: string
          format: uuid
        name:
          type: string
        mime_type:
          type: string
          example: image/jpeg
        type:
          type: string
          enum: [image, video, gif]
        url:
          type: string
          format: uri
        thumb_url:
          type: string
          format: uri
          nullable: true
        is_video:
          type: boolean
        created_at:
          type: string
          format: date-time

    Tag:
      type: object
      required: [id, uuid, name, hex_color]
      properties:
        id:
          type: integer
        uuid:
          type: string
          format: uuid
        name:
          type: string
          example: Campaign 2025
        hex_color:
          type: string
          example: '#3B82F6'

    TagInput:
      type: object
      required: [name, hex_color]
      properties:
        name:
          type: string
        hex_color:
          type: string
          example: '#3B82F6'

    PostStatus:
      type: string
      enum: [draft, scheduled, published]

    DeleteMode:
      type: string
      enum: [app_only, app_and_social, social_only]
      default: app_only
      description: |
        - `app_only` — delete from PostBoost only
        - `app_and_social` — delete from PostBoost and the social platform
        - `social_only` — delete from the social platform only

    DeleteResult:
      type: object
      required: [deleted, deleted_from_app, to_trash]
      properties:
        deleted:
          type: boolean
        deleted_from_app:
          type: boolean
        to_trash:
          type: boolean

    ScheduleResult:
      type: object
      required: [success]
      properties:
        success:
          type: boolean
        scheduled_at:
          type: string
          format: date-time
          example: '2025-06-01T10:00:00Z'

    PostContent:
      type: object
      properties:
        body:
          type: string
          nullable: true
          description: Post text/caption.
        url:
          type: string
          format: uri
          nullable: true
        media:
          type: array
          items:
            type: integer
          description: Array of media IDs from the media library.
        video_thumbs:
          type: array
          items:
            type: object

    PostVersion:
      type: object
      required: [account_id, is_original, content]
      properties:
        account_id:
          type: integer
          description: Use `0` for the original/default version.
        is_original:
          type: boolean
          description: Must be `true` for the first version.
        content:
          type: array
          items:
            $ref: '#/components/schemas/PostContent'
        options:
          type: object
          description: Platform-specific publishing options.

    PostInput:
      type: object
      required: [versions]
      properties:
        versions:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/PostVersion'
          description: "At least one version with `is_original: true` is required."
        accounts:
          type: array
          items:
            type: integer
          description: Account IDs to publish to.
        tags:
          type: array
          items:
            type: integer
          description: Tag IDs to attach.
        date:
          type: string
          format: date
          example: '2025-06-01'
        time:
          type: string
          example: '10:00'
        timezone:
          type: string
          example: America/New_York
        schedule:
          type: boolean
          description: Schedule the post for the given date/time.
        schedule_now:
          type: boolean
          description: Publish immediately.
        queue:
          type: boolean
          description: Add to the smart publishing queue.

    Post:
      type: object
      required: [id, uuid, status, accounts, versions, tags, created_at, trashed]
      properties:
        id:
          type: integer
        uuid:
          type: string
          format: uuid
        status:
          $ref: '#/components/schemas/PostStatus'
        accounts:
          type: array
          items:
            $ref: '#/components/schemas/Account'
        versions:
          type: array
          items:
            $ref: '#/components/schemas/PostVersion'
        tags:
          type: array
          items:
            $ref: '#/components/schemas/Tag'
        scheduled_at:
          type: string
          format: date-time
          nullable: true
        published_at:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time
        trashed:
          type: boolean

    WorkspaceInput:
      type: object
      required: [name, hex_color, access_status]
      properties:
        name:
          type: string
          maxLength: 60
        hex_color:
          type: string
          example: '#1d4ed8'
        owner_id:
          type: integer
          nullable: true
        access_status:
          type: string
          enum: [subscription, unlimited, locked]

    WorkspaceUserInput:
      type: object
      required: [user_id, role, can_approve, is_owner]
      properties:
        user_id:
          type: integer
        role:
          type: string
          enum: [admin, member, viewer]
        can_approve:
          type: boolean
        is_owner:
          type: boolean

    Workspace:
      type: object
      required: [uuid, name, hex_color, access_status, created_at]
      properties:
        uuid:
          type: string
          format: uuid
        name:
          type: string
        hex_color:
          type: string
        owner:
          $ref: '#/components/schemas/User'
        access_status:
          type: string
          enum: [subscription, unlimited, locked]
        created_at:
          type: string
          format: date-time

    SubscriptionStatus:
      type: string
      enum: [active, trialing, past_due, paused, canceled, incomplete]

    SubscriptionInput:
      type: object
      required: [platform_subscription_id, platform_plan_id, status]
      properties:
        platform_subscription_id:
          type: string
        platform_plan_id:
          type: string
        status:
          $ref: '#/components/schemas/SubscriptionStatus'
        trial_ends_at:
          type: string
          format: date-time
          description: Required when status is `trialing`.

    SubscriptionUpdateInput:
      type: object
      required: [platform_plan_id, status]
      properties:
        platform_plan_id:
          type: string
        status:
          $ref: '#/components/schemas/SubscriptionStatus'
        trial_ends_at:
          type: string
          format: date-time
          description: Required when status is `trialing`.
        paused_from:
          type: string
          format: date-time
          description: Required when status is `paused`.

    Subscription:
      type: object
      required: [name, platform_subscription_id, platform_plan_id, status, recurring]
      properties:
        name:
          type: string
        platform_subscription_id:
          type: string
        platform_plan_id:
          type: string
        status:
          $ref: '#/components/schemas/SubscriptionStatus'
        recurring:
          type: boolean
        trial_ends_at:
          type: string
          format: date-time
          nullable: true
        paused_from:
          type: string
          format: date-time
          nullable: true
        ends_at:
          type: string
          format: date-time
          nullable: true

    UserInput:
      type: object
      required: [name, email, password, password_confirmation, is_admin]
      properties:
        name:
          type: string
          maxLength: 255
        email:
          type: string
          format: email
        password:
          type: string
          format: password
        password_confirmation:
          type: string
          format: password
        is_admin:
          type: boolean

    UserUpdateInput:
      type: object
      required: [name, email, is_admin]
      properties:
        name:
          type: string
        email:
          type: string
          format: email
        password:
          type: string
          format: password
          nullable: true
          description: Omit or set to null to keep the current password.
        password_confirmation:
          type: string
          format: password
        is_admin:
          type: boolean

    User:
      type: object
      required: [id, name, email, is_admin, created_at]
      properties:
        id:
          type: integer
        name:
          type: string
        email:
          type: string
          format: email
        is_admin:
          type: boolean
        email_verified_at:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time

    ReceiptInput:
      type: object
      required: [workspace_uuid, transaction_id, invoice_number, amount, tax, currency, paid_at]
      properties:
        workspace_uuid:
          type: string
          format: uuid
        transaction_id:
          type: string
        invoice_number:
          type: string
        amount:
          type: number
        tax:
          type: number
        currency:
          type: string
          example: USD
        description:
          type: string
          nullable: true
        receipt_url:
          type: string
          format: uri
          nullable: true
        paid_at:
          type: string
          format: date

    ReceiptUpdateInput:
      type: object
      required: [transaction_id, invoice_number, amount, currency, paid_at]
      properties:
        transaction_id:
          type: string
        invoice_number:
          type: string
        amount:
          type: number
        tax:
          type: number
          nullable: true
        currency:
          type: string
        description:
          type: string
          nullable: true
        receipt_url:
          type: string
          format: uri
          nullable: true
        paid_at:
          type: string
          format: date

    Receipt:
      type: object
      required: [uuid, transaction_id, invoice_number, amount, tax, currency, paid_at, created_at]
      properties:
        uuid:
          type: string
          format: uuid
        transaction_id:
          type: string
        invoice_number:
          type: string
        amount:
          type: number
        tax:
          type: number
        currency:
          type: string
        receipt_url:
          type: string
          format: uri
          nullable: true
        description:
          type: string
          nullable: true
        paid_at:
          type: string
          format: date-time
        created_at:
          type: string
          format: date-time
