> ## Documentation Index
> Fetch the complete documentation index at: https://docs.uplint.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Add Storage Bucket

> Register a new S3 bucket for the tenant. Unlike single-bucket configuration, this does **not** deactivate existing buckets. The first bucket added — or any created with `set_as_default: true` — becomes the tenant's default (fallback) bucket.



## OpenAPI

````yaml POST /api/v1/tenants/{tenant_id}/storage-configs
openapi: 3.0.3
info:
  title: Uplint API
  version: 0.1.0
  description: >-
    Uplint is a file safety and integrity platform. Upload files through
    context-aware pipelines that automatically validate integrity, detect blank
    or placeholder content, and scan for viruses — all behind a single API.


    This specification covers the core Uplint REST API:


    - **API Keys** — Create and manage scoped API keys for programmatic access.

    - **File Contexts** — Define upload pipelines with per-context rules
    (allowed extensions, size limits, blank/corrupt rejection, virus scanning,
    rate limits, and storage quotas).

    - **Files** — Upload, list, download, inspect, and delete files. Every
    upload runs through the validation pipeline configured on its context.


    All endpoints are tenant-scoped. Authenticate with an API key or JWT token
    via the `Authorization` header.
  contact:
    name: Uplint Team
    url: https://uplint.dev
  license:
    name: MIT
    url: https://opensource.org/licenses/MIT
servers:
  - url: https://api.uplint.dev
    description: Production
  - url: http://localhost:8000
    description: Local development
security:
  - BearerAuth: []
tags:
  - name: API Keys
    description: >-
      Create, list, inspect, update, and revoke API keys. Each key carries
      scoped permissions (`upload`, `download`, `metadata`, `delete`, `admin`)
      and is bound to the authenticated tenant.
  - name: File Contexts
    description: >-
      File contexts define upload pipelines. Each context specifies allowed
      extensions, max file size, whether to reject blank or corrupt files, virus
      scanning, rate limits, and storage quotas. Files are always uploaded into
      a context.
  - name: Files
    description: >-
      Upload, list, download, inspect metadata, and delete files. Every upload
      is validated against its context rules before storage.
  - name: Storage Buckets
    description: >-
      Manage the S3 buckets a tenant stores files in. Each tenant can register
      multiple buckets (bring-your-own S3 credentials); one is the **default**
      (fallback) bucket, and file contexts can be routed to any active bucket.
      All storage endpoints require an `admin`-scoped API key.
paths:
  /api/v1/tenants/{tenant_id}/storage-configs:
    post:
      tags:
        - Storage Buckets
      summary: Add a storage bucket
      description: >-
        Register a new S3 bucket for the tenant. Unlike single-bucket
        configuration, this does **not** deactivate existing buckets. The first
        bucket added — or any created with `set_as_default: true` — becomes the
        tenant's default (fallback) bucket.
      operationId: addStorageBucket
      parameters:
        - $ref: '#/components/parameters/TenantId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateStorageConfigRequest'
            example:
              name: Patient Documents
              access_key_id: AKIAIOSFODNN7EXAMPLE
              secret_access_key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
              region: ap-south-1
              default_bucket: acme-patient-docs
              base_path: file-service/
              set_as_default: false
      responses:
        '201':
          description: Storage bucket added.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse_StorageConfig'
              example:
                status: SUCCESS
                message: Storage bucket added successfully
                data:
                  config_id: cfg_a1b2c3d4e5f6
                  tenant_id: tnt_9f8e7d6c
                  name: Patient Documents
                  provider: aws_s3
                  access_key_id: AKIA...MPLE
                  region: ap-south-1
                  default_bucket: acme-patient-docs
                  base_path: file-service/
                  is_active: true
                  is_default: false
                  created_at: '2026-02-10T12:00:00Z'
                  updated_at: null
                timestamp: '2026-02-10T12:00:00Z'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          description: A bucket with this `name` already exists for the tenant.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          $ref: '#/components/responses/ValidationError'
      security:
        - BearerAuth: []
components:
  parameters:
    TenantId:
      name: tenant_id
      in: path
      required: true
      schema:
        type: string
      description: The tenant's unique identifier.
  schemas:
    CreateStorageConfigRequest:
      type: object
      required:
        - name
        - access_key_id
        - secret_access_key
        - default_bucket
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 100
          description: Human-friendly label for this bucket (unique per tenant).
        set_as_default:
          type: boolean
          default: false
          description: >-
            Make this the tenant's default (fallback) bucket. The first bucket
            added always becomes the default.
        access_key_id:
          type: string
          minLength: 16
          maxLength: 128
          description: AWS access key ID.
        secret_access_key:
          type: string
          minLength: 1
          description: AWS secret access key. Encrypted at rest and never returned.
        region:
          type: string
          default: ap-south-1
          description: AWS region of the bucket.
        default_bucket:
          type: string
          minLength: 3
          maxLength: 63
          description: S3 bucket name. Immutable after creation.
        base_path:
          type: string
          default: file-service/
          description: >-
            Key prefix applied to every object written to this bucket. Immutable
            after creation.
    SuccessResponse_StorageConfig:
      type: object
      properties:
        status:
          type: string
          enum:
            - SUCCESS
        message:
          type: string
        data:
          $ref: '#/components/schemas/StorageConfigResponse'
        timestamp:
          type: string
          format: date-time
    ErrorResponse:
      type: object
      properties:
        status:
          type: string
          enum:
            - ERROR
        message:
          type: string
        errors:
          type: array
          items:
            type: string
        timestamp:
          type: string
          format: date-time
    StorageConfigResponse:
      type: object
      properties:
        config_id:
          type: string
          description: Configuration identifier.
        tenant_id:
          type: string
        name:
          type: string
          description: Human-friendly bucket label.
        provider:
          type: string
          enum:
            - aws_s3
          description: Storage provider.
        access_key_id:
          type: string
          description: AWS access key ID, masked (e.g. `AKIA...MPLE`).
        region:
          type: string
        default_bucket:
          type: string
          description: S3 bucket name.
        base_path:
          type: string
          description: Key prefix applied to objects in this bucket.
        is_active:
          type: boolean
          description: Whether this bucket is available for routing.
        is_default:
          type: boolean
          description: Whether this is the tenant's default (fallback) bucket.
        created_at:
          type: string
          format: date-time
          nullable: true
        updated_at:
          type: string
          format: date-time
          nullable: true
  responses:
    Unauthorized:
      description: Missing, invalid, or expired authentication credentials.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            status: ERROR
            message: Invalid or missing API key
            errors:
              - Authentication required
            timestamp: '2026-02-10T12:00:00Z'
    Forbidden:
      description: The API key lacks the required `admin` scope.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            status: ERROR
            message: Admin scope required
            errors:
              - Insufficient permissions
            timestamp: '2026-02-10T12:00:00Z'
    ValidationError:
      description: Request body failed validation.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            status: ERROR
            message: Validation error
            errors:
              - 'name: field required'
            timestamp: '2026-02-10T12:00:00Z'
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: >-
        Pass an API key or JWT token. API keys can be sent as `Authorization:
        Bearer <key>` or `Authorization: <key>`.

````