openapi: 3.1.0
info:
  title: BundleUp API
  description: REST API for managing integrations, connections, and webhooks in a BundleUp workspace.
  version: 1.0.0
  contact:
    name: BundleUp Support
    url: https://www.bundleup.io
    email: support@bundleup.io
  license:
    name: MIT
    url: https://opensource.org/licenses/MIT

servers:
  - url: https://api.bundleup.io/v1
    description: Production API

security:
  - bearerAuth: []
  - basicAuth: []

tags:
  - name: Integrations
    description: Integration discovery operations
  - name: Connections
    description: Connection management operations
  - name: Webhooks
    description: Webhook management operations

paths:
  /integrations:
    get:
      tags:
        - Integrations
      summary: List integrations
      description: Returns integrations for the authenticated workspace.
      parameters:
        - $ref: '#/components/parameters/Offset'
        - $ref: '#/components/parameters/Limit'
        - name: status
          in: query
          description: Accepted by the API and currently ignored by the handler.
          schema:
            type: string
      responses:
        '200':
          description: Integrations retrieved successfully.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Integration'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /integrations/{id}:
    get:
      tags:
        - Integrations
      summary: Get integration
      description: Returns a single integration for the authenticated workspace.
      parameters:
        - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Integration retrieved successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Integration'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /connections:
    get:
      tags:
        - Connections
      summary: List connections
      description: Returns connections for the authenticated workspace.
      parameters:
        - $ref: '#/components/parameters/Offset'
        - $ref: '#/components/parameters/Limit'
        - name: integration_id
          in: query
          description: Filter by integration ID.
          schema:
            type: string
        - name: integration_identifier
          in: query
          description: Filter by integration identifier.
          schema:
            type: string
        - name: external_id
          in: query
          description: Filter by external ID.
          schema:
            type: string
      responses:
        '200':
          description: Connections retrieved successfully.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Connection'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

    post:
      tags:
        - Connections
      summary: Create connection
      description: Creates a connection for the authenticated workspace.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateConnectionRequest'
      responses:
        '200':
          description: Connection created successfully.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: false
                properties:
                  data:
                    $ref: '#/components/schemas/Connection'
                required:
                  - data
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /connections/{id}:
    get:
      tags:
        - Connections
      summary: Get connection
      description: Returns a single connection for the authenticated workspace.
      parameters:
        - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Connection retrieved successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Connection'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

    patch:
      tags:
        - Connections
      summary: Update connection
      description: Updates a connection for the authenticated workspace.
      parameters:
        - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateConnectionRequest'
      responses:
        '200':
          description: Connection updated successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Connection'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

    delete:
      tags:
        - Connections
      summary: Delete connection
      description: Deletes a connection and attempts to revoke the provider token.
      parameters:
        - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Connection deleted successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /webhooks:
    get:
      tags:
        - Webhooks
      summary: List webhooks
      description: Returns webhooks for the authenticated workspace.
      parameters:
        - $ref: '#/components/parameters/Offset'
        - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Webhooks retrieved successfully.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Webhook'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

    post:
      tags:
        - Webhooks
      summary: Create webhook
      description: Creates a webhook for the authenticated workspace.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateWebhookRequest'
      responses:
        '200':
          description: Webhook created successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Webhook'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /webhooks/{id}:
    get:
      tags:
        - Webhooks
      summary: Get webhook
      description: Returns a single webhook for the authenticated workspace.
      parameters:
        - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Webhook retrieved successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Webhook'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

    patch:
      tags:
        - Webhooks
      summary: Update webhook
      description: Updates a webhook for the authenticated workspace.
      parameters:
        - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateWebhookRequest'
      responses:
        '200':
          description: Webhook updated successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Webhook'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

    delete:
      tags:
        - Webhooks
      summary: Delete webhook
      description: Deletes a webhook for the authenticated workspace.
      parameters:
        - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Webhook deleted successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API Key
      description: Pass the API key as a bearer token.
    basicAuth:
      type: http
      scheme: basic
      description: Pass any username and the API key as the password.

  parameters:
    Id:
      name: id
      in: path
      required: true
      description: Resource identifier.
      schema:
        type: string
    Offset:
      name: offset
      in: query
      description: Number of items to skip.
      schema:
        type: integer
        minimum: 0
        maximum: 1000
        default: 0
    Limit:
      name: limit
      in: query
      description: Maximum number of items to return.
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 10

  schemas:
    Integration:
      type: object
      additionalProperties: false
      properties:
        id:
          type: string
          description: Integration identifier.
        identifier:
          type: string
          description: Integration slug or unique identifier.
        type:
          type: string
          description: Integration type.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - identifier
        - type
        - created_at
        - updated_at

    Connection:
      type: object
      additionalProperties: false
      properties:
        id:
          type: string
          description: Connection identifier.
        external_id:
          type:
            - string
            - 'null'
          description: External system identifier.
        expires_at:
          type:
            - string
            - 'null'
          format: date-time
          description: Expiration timestamp for the access token.
        integration_id:
          type: string
          description: Integration ID associated with the connection.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - external_id
        - expires_at
        - integration_id
        - created_at
        - updated_at

    Webhook:
      type: object
      additionalProperties: false
      properties:
        id:
          type: string
          description: Webhook identifier.
        name:
          type: string
        url:
          type: string
          format: uri
        events:
          $ref: '#/components/schemas/WebhookEvents'
        last_triggered_at:
          type:
            - string
            - 'null'
          format: date-time
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - name
        - url
        - events
        - last_triggered_at
        - created_at
        - updated_at

    WebhookEvents:
      type: object
      additionalProperties: false
      properties:
        integration.created:
          type: boolean
        integration.updated:
          type: boolean
        integration.deleted:
          type: boolean
        connection.created:
          type: boolean
        connection.updated:
          type: boolean
        connection.deleted:
          type: boolean
        connection.refreshed:
          type: boolean
        connection.disabled:
          type: boolean

    CreateConnectionRequest:
      type: object
      additionalProperties: false
      properties:
        integration_id:
          type: string
          description: Integration ID to associate with the connection.
        auth_token:
          type: string
          description: Provider access token.
        external_id:
          type: string
          description: External account identifier.
        refresh_token:
          type: string
          description: Provider refresh token.
        expires_in:
          type: number
          description: Access token lifetime in seconds.
      required:
        - integration_id
        - auth_token

    UpdateConnectionRequest:
      type: object
      additionalProperties: false
      properties:
        external_id:
          type: string
          description: External account identifier.

    CreateWebhookRequest:
      type: object
      additionalProperties: false
      properties:
        name:
          type: string
          minLength: 3
          maxLength: 50
          description: Webhook display name.
        url:
          type: string
          format: uri
          description: Destination URL for webhook deliveries.
        events:
          allOf:
            - $ref: '#/components/schemas/WebhookEvents'
          description: Event flags. At least one event must be set to true.
      required:
        - name
        - url
        - events

    UpdateWebhookRequest:
      type: object
      additionalProperties: false
      properties:
        name:
          type: string
          minLength: 3
          maxLength: 50
          description: Webhook display name.
        url:
          type: string
          format: uri
          description: Destination URL for webhook deliveries.
        events:
          allOf:
            - $ref: '#/components/schemas/WebhookEvents'
          description: Event flags. At least one event must be set to true.
      required:
        - name
        - url
        - events

    SuccessResponse:
      type: object
      additionalProperties: false
      properties:
        success:
          type: boolean
          const: true
      required:
        - success

    ErrorResponse:
      type: object
      additionalProperties: false
      properties:
        success:
          type: boolean
          const: false
        message:
          type: string
      required:
        - success
        - message

    ValidationErrorResponse:
      allOf:
        - $ref: '#/components/schemas/ErrorResponse'
        - type: object
          properties:
            errors:
              type: object
              additionalProperties:
                type: array
                items:
                  type: string
          required:
            - errors

  responses:
    BadRequest:
      description: Invalid request payload or query parameters.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ValidationErrorResponse'
    Unauthorized:
      description: Missing or invalid API credentials.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    NotFound:
      description: The requested resource was not found.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
