openapi: 3.1.0
info:
  title: APIHUB Sandbox API
  version: 0.1.0
  license:
    name: Proprietary
    identifier: LicenseRef-Proprietary
  description: Executable contract for the sandbox vertical slice; data and provider behavior are synthetic.
servers:
  - url: http://127.0.0.1:3000/api
    description: Local development
security: []
tags:
  - { name: System, description: Service health and runtime metadata. }
  - { name: Profiles, description: Organizational boundaries for accounts and content. }
  - { name: Accounts, description: Connected provider identities and capabilities. }
  - { name: Connections, description: Provider connection sessions. }
  - { name: Posts, description: Multi-target publishing and scheduling operations. }
paths:
  /v1/health:
    get:
      tags: [System]
      operationId: getHealth
      summary: Inspect service health
      responses:
        "200":
          description: Service is ready
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/Health"
        "400":
          $ref: "#/components/responses/ValidationError"
  /v1/profiles:
    get:
      tags: [Profiles]
      operationId: listProfiles
      summary: List workspace profiles
      responses:
        "200":
          $ref: "#/components/responses/ProfileList"
        "400":
          $ref: "#/components/responses/ValidationError"
  /v1/accounts:
    get:
      tags: [Accounts]
      operationId: listAccounts
      summary: List connected accounts
      parameters:
        - in: query
          name: profileId
          schema:
            type: string
          description: Restrict results to one profile.
      responses:
        "200":
          description: Connected account list
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/Envelope"
                  - type: object
                    properties:
                      data:
                        type: array
                        items: { $ref: "#/components/schemas/Account" }
        "400":
          $ref: "#/components/responses/ValidationError"
  /v1/connect-sessions:
    post:
      tags: [Connections]
      operationId: createConnectSession
      summary: Begin a self-service or delegated connection
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [profileId, provider]
              properties:
                profileId:
                  type: string
                provider:
                  type: string
                mode:
                  type: string
                  enum: [self, invite]
                  default: self
      responses:
        "201":
          description: Sandbox connected or invite created
        "202":
          description: Provider credentials are required before OAuth
        "422":
          $ref: "#/components/responses/ValidationError"
  /v1/posts:
    get:
      tags: [Posts]
      operationId: listPosts
      summary: List posts
      parameters:
        - in: query
          name: profileId
          schema:
            type: string
      responses:
        "200":
          description: Post list
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/Envelope"
                  - type: object
                    properties:
                      data:
                        type: array
                        items: { $ref: "#/components/schemas/Post" }
        "400":
          $ref: "#/components/responses/ValidationError"
    post:
      tags: [Posts]
      operationId: createPost
      summary: Create a draft, immediate, scheduled, or queued post
      parameters:
        - { in: header, name: Idempotency-Key, required: true, schema: { type: string, minLength: 8 } }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreatePost"
      responses:
        "201":
          description: Draft created
        "202":
          description: Publish, schedule, or queue intent accepted
        "409":
          description: Idempotency key reused with different input
        "422":
          $ref: "#/components/responses/ValidationError"
components:
  schemas:
    Envelope:
      type: object
      required: [data, meta]
      properties:
        data: {}
        meta:
          type: object
          required: [requestId]
          properties:
            requestId:
              type: string
    Health:
      type: object
      required: [status, service, version, time]
      properties:
        status: { const: ok }
        service: { type: string }
        version: { type: string }
        time: { type: string, format: date-time }
    Profile:
      type: object
      required: [id, name, description, color]
      properties:
        id: { type: string }
        name: { type: string }
        description: { type: string }
        color: { type: string }
    Account:
      type: object
      required: [id, profileId, platformId, displayName, handle, health, capabilities]
      properties:
        id: { type: string }
        profileId: { type: string }
        platformId: { type: string }
        displayName: { type: string }
        handle: { type: string }
        health: { type: string, enum: [healthy, degraded, action_required] }
        capabilities: { type: array, items: { type: string } }
    CreatePost:
      type: object
      required: [profileId, content, accountIds, intent]
      properties:
        profileId: { type: string }
        content: { type: string, minLength: 1, maxLength: 5000 }
        accountIds: { type: array, maxItems: 20, items: { type: string } }
        intent: { type: string, enum: [draft, publish_now, scheduled, queued] }
        scheduledFor: { type: string, format: date-time }
    PostTarget:
      type: object
      required: [id, accountId, platformId, status]
      properties:
        id: { type: string }
        accountId: { type: string }
        platformId: { type: string }
        status: { type: string, enum: [pending, queued, publishing, published, retry_wait, failed, cancelled] }
        providerPostUrl: { type: string, format: uri }
        errorCode: { type: string }
    Post:
      allOf:
        - $ref: "#/components/schemas/CreatePost"
        - type: object
          required: [id, status, createdAt, updatedAt, createdBy, targets]
          properties:
            id: { type: string }
            status: { type: string, enum: [draft, scheduled, queued, publishing, published, partial, failed, cancelled] }
            createdAt: { type: string, format: date-time }
            updatedAt: { type: string, format: date-time }
            createdBy: { type: string }
            targets: { type: array, items: { $ref: "#/components/schemas/PostTarget" } }
    Error:
      type: object
      required: [error]
      properties:
        error:
          type: object
          required: [code, message, requestId, retryable]
          properties:
            code: { type: string }
            message: { type: string }
            details: {}
            requestId: { type: string }
            retryable: { type: boolean }
  responses:
    ProfileList:
      description: Profile list
      content:
        application/json:
          schema:
            allOf:
              - $ref: "#/components/schemas/Envelope"
              - type: object
                properties:
                  data:
                    type: array
                    items: { $ref: "#/components/schemas/Profile" }
    ValidationError:
      description: Request validation failed
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
