openapi: 3.1.0
info:
  title: Cascade Social API
  version: 1.0.0
  description: |
    Self-hosted social media scheduling platform (Buffer alternative).
    Authenticate with `Authorization: Bearer <api-key>`.
    Scopes: read, write, admin. Mint scoped keys via POST /keys (admin).
servers:
  - url: http://localhost:4820/api/v1
security:
  - bearerAuth: []
components:
  securitySchemes:
    bearerAuth: { type: http, scheme: bearer }
  schemas:
    Post:
      type: object
      properties:
        id: { type: string }
        status: { type: string, enum: [draft, scheduled, publishing, published, partial, failed, canceled] }
        content: { type: string }
        contentByChannel: { type: object, description: "Per-platform overrides, e.g. {\"x\": \"280-char version\"}" }
        media: { type: array, items: { type: object, properties: { url: {type: string}, alt: {type: string}, type: {type: string} } } }
        channelIds: { type: array, items: { type: string } }
        scheduledAt: { type: [string, "null"], format: date-time }
        publishedAt: { type: [string, "null"], format: date-time }
        results: { type: object, description: "Per-channel publish results" }
        tags: { type: array, items: { type: string } }
    Channel:
      type: object
      properties:
        id: { type: string }
        name: { type: string }
        platform: { type: string, enum: [x, mastodon, linkedin, facebook, instagram, telegram, discord, webhook, console] }
        timezone: { type: string }
        schedule_json: { type: string, description: "Posting slots JSON: [{dow:[1,2,3,4,5], times:[\"09:00\"]}]" }
        status: { type: string, enum: [active, paused] }
paths:
  /health:
    get: { summary: Health check (no auth), security: [], responses: { "200": { description: OK } } }
  /platforms:
    get: { summary: List supported platforms and required credential fields, responses: { "200": { description: OK } } }
  /channels:
    get: { summary: List channels, responses: { "200": { description: OK } } }
    post:
      summary: Create channel
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required: [name, platform]
              properties:
                name: { type: string }
                platform: { type: string }
                credentials: { type: object, description: Platform credentials (encrypted at rest) }
                timezone: { type: string }
                schedule: { type: array, items: { type: object, properties: { dow: { type: array, items: {type: integer} }, times: { type: array, items: {type: string} } } } }
      responses: { "201": { description: Created } }
  /channels/{id}:
    patch: { summary: Update channel, parameters: [{name: id, in: path, required: true, schema: {type: string}}], responses: { "200": { description: OK } } }
    delete: { summary: Delete channel, parameters: [{name: id, in: path, required: true, schema: {type: string}}], responses: { "200": { description: OK } } }
  /channels/{id}/test:
    post: { summary: Send a live test post through the channel, parameters: [{name: id, in: path, required: true, schema: {type: string}}], responses: { "200": { description: OK } } }
  /channels/{id}/next-slot:
    get: { summary: Next open queue slot, parameters: [{name: id, in: path, required: true, schema: {type: string}}], responses: { "200": { description: OK } } }
  /posts:
    get:
      summary: List posts
      parameters:
        - { name: status, in: query, schema: { type: string } }
        - { name: channel_id, in: query, schema: { type: string } }
        - { name: limit, in: query, schema: { type: integer, default: 50 } }
      responses: { "200": { description: OK } }
    post:
      summary: Create a post (draft, publish now, schedule at a time, or add to queue)
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required: [content]
              properties:
                content: { type: string }
                contentByChannel: { type: object }
                media: { type: array, items: { type: object } }
                channelIds: { type: array, items: { type: string } }
                schedule: { type: string, enum: [draft, now, at, queue], default: draft }
                scheduledAt: { type: string, format: date-time, description: Required when schedule=at }
                tags: { type: array, items: { type: string } }
      responses: { "201": { description: Created } }
  /posts/{id}:
    get: { summary: Get post, parameters: [{name: id, in: path, required: true, schema: {type: string}}], responses: { "200": { description: OK } } }
    patch: { summary: Edit post, parameters: [{name: id, in: path, required: true, schema: {type: string}}], responses: { "200": { description: OK } } }
    delete: { summary: Delete post, parameters: [{name: id, in: path, required: true, schema: {type: string}}], responses: { "200": { description: OK } } }
  /posts/{id}/publish:
    post: { summary: Publish immediately, parameters: [{name: id, in: path, required: true, schema: {type: string}}], responses: { "200": { description: OK } } }
  /posts/{id}/cancel:
    post: { summary: Cancel a scheduled post, parameters: [{name: id, in: path, required: true, schema: {type: string}}], responses: { "200": { description: OK } } }
  /queue:
    get: { summary: All scheduled posts in order, responses: { "200": { description: OK } } }
  /ideas:
    get: { summary: List ideas, responses: { "200": { description: OK } } }
    post: { summary: Capture an idea, responses: { "201": { description: Created } } }
  /ideas/{id}:
    patch: { summary: Update idea, parameters: [{name: id, in: path, required: true, schema: {type: string}}], responses: { "200": { description: OK } } }
  /analytics/summary:
    get:
      summary: Publish counts by status, channel, and day
      parameters: [{ name: days, in: query, schema: { type: integer, default: 30 } }]
      responses: { "200": { description: OK } }
  /events:
    get: { summary: Audit log, responses: { "200": { description: OK } } }
  /keys:
    get: { summary: List API keys (admin), responses: { "200": { description: OK } } }
    post: { summary: Mint a scoped API key (admin), responses: { "201": { description: Created } } }
  /keys/{id}:
    delete: { summary: Revoke key (admin), parameters: [{name: id, in: path, required: true, schema: {type: string}}], responses: { "200": { description: OK } } }
