openapi: 3.1.0
info:
  title: BuildCheap Instant Deploy API
  version: "1.0"
  description: |
    Deploy static sites to live *.buildcheap.host URLs. Anonymous deploys
    expire after 1 hour unless claimed; API-key deploys are permanent.
servers:
  - url: https://api.buildcheap.host
paths:
  /v1/deploys:
    post:
      summary: Deploy a static site
      description: |
        Multipart upload of site files. Each part is named "files" with the
        file's site-relative path as its filename. index.html is required.
        Without auth the deploy is anonymous (expires in 1 hour, claimable).
        With Authorization: Bearer bc_live_... the deploy is permanent under
        the key owner's account; pass ?slug= to update an existing project.
      parameters:
        - in: query
          name: slug
          required: false
          schema: { type: string }
          description: (API key only) target an existing project's slug.
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                files:
                  type: array
                  items: { type: string, format: binary }
      responses:
        "201":
          description: Deployed
          content:
            application/json:
              schema:
                type: object
                properties:
                  slug: { type: string }
                  url: { type: string }
                  expires_at: { type: string, nullable: true }
                  claim_url: { type: string, nullable: true }
                  deploy_token: { type: string, nullable: true }
        "400": { description: Validation failed (missing index.html, bad path, too many files) }
        "401": { description: Invalid or revoked API key }
        "413": { description: Payload too large }
        "429": { description: Rate limited }
  /v1/deploys/{slug}:
    get:
      summary: Get deploy status
      parameters:
        - { in: path, name: slug, required: true, schema: { type: string } }
      responses:
        "200":
          description: Live
          content:
            application/json:
              schema:
                type: object
                properties:
                  url: { type: string }
                  expires_at: { type: string, nullable: true }
        "404": { description: No site at this slug (or expired) }
    put:
      summary: Update an anonymous deploy
      description: Re-upload files for an existing anonymous deploy. Refreshes the 1-hour expiry.
      parameters:
        - { in: path, name: slug, required: true, schema: { type: string } }
        - in: header
          name: X-Deploy-Token
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                files:
                  type: array
                  items: { type: string, format: binary }
      responses:
        "200": { description: Updated }
        "403": { description: Bad deploy token }
        "404": { description: Unknown slug }
        "410": { description: Deploy expired — create a new one }
  /v1/me:
    get:
      summary: Verify a Data API key
      description: "Auth test for bc_data_ keys. Authorization: Bearer bc_data_..."
      security: [{ dataKey: [] }]
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  id: { type: string }
                  email: { type: string, nullable: true }
        "401": { description: Invalid or revoked Data API key }
  /v1/projects:
    get:
      summary: List your projects (Data API)
      security: [{ dataKey: [] }]
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    id: { type: string }
                    name: { type: string }
                    slug: { type: string, nullable: true }
  /v1/projects/{id}/collections:
    get:
      summary: List a project's Cloud collections
      security: [{ dataKey: [] }]
      parameters:
        - { in: path, name: id, required: true, schema: { type: string } }
      responses:
        "200":
          description: Collection names
          content:
            application/json:
              schema: { type: array, items: { type: string } }
        "404": { description: Project not found or not yours }
  /v1/projects/{id}/collections/{collection}/records:
    get:
      summary: List recent records (newest first)
      security: [{ dataKey: [] }]
      parameters:
        - { in: path, name: id, required: true, schema: { type: string } }
        - { in: path, name: collection, required: true, schema: { type: string } }
        - { in: query, name: limit, required: false, schema: { type: integer, default: 50 } }
      responses:
        "200": { description: Records }
    post:
      summary: Create a record (lead/row)
      security: [{ dataKey: [] }]
      parameters:
        - { in: path, name: id, required: true, schema: { type: string } }
        - { in: path, name: collection, required: true, schema: { type: string } }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                data: { type: object, additionalProperties: true }
      responses:
        "201": { description: Created }
  /v1/hooks:
    post:
      summary: Subscribe to new-record events (REST Hook)
      security: [{ dataKey: [] }]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [project_id, collection, target_url]
              properties:
                project_id: { type: string }
                collection: { type: string }
                target_url: { type: string, format: uri }
      responses:
        "201": { description: "Subscribed — returns the hook id" }
  /v1/hooks/{id}:
    delete:
      summary: Unsubscribe
      security: [{ dataKey: [] }]
      parameters:
        - { in: path, name: id, required: true, schema: { type: string } }
      responses:
        "200": { description: Unsubscribed }
        "404": { description: Hook not found }
components:
  securitySchemes:
    dataKey:
      type: http
      scheme: bearer
      description: A bc_data_ Data API key (Settings → Integrations).