openapi: 3.1.0
info:
  title: OnPin API
  version: 0.1.0
  description: Public API for publishing static artifacts to shareable URLs.
servers:
  - url: https://api.onpin.live
paths:
  /v0/publish:
    post:
      summary: Publish an artifact
      description: Publish a static file or bundle. Supports anonymous (no auth, 24h expiry) and authenticated (API key, no expiry) publishing.
      security:
        - bearerAuth: []
        - {}
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PublishRequest"
      responses:
        "201":
          description: Published
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PublishResponse"
        "400":
          $ref: "#/components/responses/PublishError"
        "429":
          description: Rate limited
  /v0/publish/{deployment_id}:
    get:
      summary: Get deployment status
      parameters:
        - $ref: "#/components/parameters/DeploymentId"
      responses:
        "200":
          description: Deployment
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Deployment"
    put:
      summary: Update a deployment in place
      description: Replace the content of an existing deployment. The public URL stays the same. Requires the API key that created the deployment.
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/DeploymentId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PublishRequest"
      responses:
        "200":
          description: Updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PublishResponse"
    delete:
      summary: Delete a deployment
      description: Removes public access and deletes stored artifacts. Requires the API key that created the deployment.
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/DeploymentId"
      responses:
        "200":
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  deployment_id:
                    type: string
                  status:
                    const: deleted
  /v0/deployments:
    get:
      summary: List deployments
      description: Returns all deployments owned by the API key.
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Deployments
          content:
            application/json:
              schema:
                type: object
                properties:
                  deployments:
                    type: array
                    items:
                      $ref: "#/components/schemas/Deployment"
  /v0/auth/device:
    post:
      summary: Start device authorization
      description: Request a device code and verification URL. The agent shows the URL to the user, who opens it in a browser to authorize. No authentication required.
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
      responses:
        "200":
          description: Device code created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeviceCodeResponse"
  /v0/auth/device/token:
    post:
      summary: Poll for API key
      description: Poll with the device code to check if the user has authorized. Returns 428 while pending, 200 with API key once approved, 410 if expired.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [device_code]
              properties:
                device_code:
                  type: string
      responses:
        "200":
          description: Approved — API key returned
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeviceTokenApproved"
        "428":
          description: Pending — user has not yet authorized
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    const: pending
        "410":
          description: Expired — device code is no longer valid
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: API key obtained via the device auth flow. Send as "Authorization: Bearer <api-key>".
  parameters:
    DeploymentId:
      name: deployment_id
      in: path
      required: true
      schema:
        type: string
  responses:
    PublishError:
      description: Structured publishing error
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/PublishError"
  schemas:
    PublishRequest:
      oneOf:
        - $ref: "#/components/schemas/StaticObjectPublishRequest"
        - $ref: "#/components/schemas/StaticBundlePublishRequest"
    StaticObjectPublishRequest:
      type: object
      required: [artifact_type, name]
      properties:
        artifact_type:
          const: static_object
        name:
          type: string
          description: Filename including extension. Used to infer content_type if not provided.
        content_type:
          type: string
          description: Optional. Server infers from the file extension if omitted.
        content:
          type: string
          description: File content as a UTF-8 string. Use for text files.
        content_base64:
          type: string
          description: File content as base64. Use for binary files.
        expires_in_seconds:
          type: integer
    StaticBundlePublishRequest:
      type: object
      required: [artifact_type]
      properties:
        artifact_type:
          const: static_bundle
        files:
          type: object
          description: Map of file paths to content. Use for small bundles with text files.
          additionalProperties:
            $ref: "#/components/schemas/FilePayload"
        archive_base64:
          type: string
          description: Base64-encoded zip archive. Use for build outputs or folders with binary assets.
        entrypoint:
          type: string
          description: The file to serve at the root URL. Defaults to index.html.
        expires_in_seconds:
          type: integer
    FilePayload:
      type: object
      properties:
        content:
          type: string
        content_base64:
          type: string
        content_type:
          type: string
          description: Optional. Server infers from the filename if omitted.
    PublishResponse:
      type: object
      properties:
        deployment_id:
          type: string
        status:
          $ref: "#/components/schemas/DeploymentStatus"
        public_url:
          type: string
        expires_at:
          type: string
          format: date-time
          description: Present for anonymous deployments. Absent for authenticated deployments (no expiry).
    Deployment:
      type: object
      properties:
        id:
          type: string
        artifactType:
          type: string
        anonymous:
          type: boolean
        platformPath:
          type: string
        status:
          $ref: "#/components/schemas/DeploymentStatus"
        publicUrl:
          type: string
        expiresAt:
          type: string
          format: date-time
        provider:
          type: string
    DeploymentStatus:
      enum: [validating, building, publishing, live, failed, expired, deleted]
    PublishError:
      type: object
      required: [status, error_code, message, fix]
      properties:
        status:
          const: failed
        error_code:
          type: string
        message:
          type: string
        fix:
          type: string
    DeviceCodeResponse:
      type: object
      properties:
        device_code:
          type: string
          description: Opaque token for polling. Do not show to the user.
        user_code:
          type: string
          description: Human-readable code embedded in the verification URL.
        verification_url:
          type: string
          description: URL to show the user. They open it in a browser to authorize.
        expires_in:
          type: integer
          description: Seconds until the device code expires (600 = 10 minutes).
        interval:
          type: integer
          description: Recommended polling interval in seconds.
    DeviceTokenApproved:
      type: object
      properties:
        status:
          const: approved
        api_key:
          type: string
          description: The API key. Store to disk immediately and never print.
        api_key_id:
          type: string
