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

# Upload media

> Upload media files for WhatsApp messaging from public URLs.

Supports two delivery methods:
- `meta_media`: Standard upload to Meta's media endpoint (30-day lifetime)
- `meta_resumable_asset`: Resumable upload flow for profile pictures and large files

**Security**: SSRF-protected - blocks private IPs, localhost, and metadata endpoints

**Size limits**:
- Images: 5 MB
- Audio/Video: 16 MB
- Documents: 100 MB

Requests exceeding these limits fail immediately.




## OpenAPI

````yaml /api/platform/v1/openapi-platform.yaml post /whatsapp/media
openapi: 3.1.0
info:
  title: Kapso Platform API
  version: 0.2.0
  description: >
    Build WhatsApp messaging into your product. Manage customers, connect phone
    numbers, send broadcasts, and handle conversations.
servers:
  - url: https://api.kapso.ai/platform/v1
    description: Production
security:
  - ApiKeyAuth: []
tags:
  - name: Customers
    description: Manage customer accounts
  - name: Setup Links
    description: WhatsApp onboarding via embedded signup
  - name: Phone Numbers
    description: Connect and manage WhatsApp numbers
  - name: Webhooks
    description: Subscribe to WhatsApp events
  - name: Display Names
    description: Update WhatsApp business display names
  - name: Broadcasts
    description: |
      Send template messages at scale

      **Alpha**: This API is in alpha and subject to change
  - name: Conversations
    description: Manage conversation state
  - name: Media
    description: Upload media files for WhatsApp messaging
  - name: Users
    description: Manage project team members
  - name: Inbox Embeds
    description: Create and manage embeddable inbox access links
  - name: Webhook Deliveries
    description: View webhook delivery attempts and their status
  - name: External API Logs
    description: View logs of external API calls made by the project
  - name: Log Search
    description: Search Logs V2 events across API, Meta, workflow, and webhook sources
  - name: Provider Models
    description: List available AI provider models
  - name: WhatsApp Flows
    description: Build interactive WhatsApp Flows for surveys and forms
  - name: Contacts
    description: Manage WhatsApp contacts
paths:
  /whatsapp/media:
    post:
      tags:
        - Media
      summary: Upload media
      description: >
        Upload media files for WhatsApp messaging from public URLs.


        Supports two delivery methods:

        - `meta_media`: Standard upload to Meta's media endpoint (30-day
        lifetime)

        - `meta_resumable_asset`: Resumable upload flow for profile pictures and
        large files


        **Security**: SSRF-protected - blocks private IPs, localhost, and
        metadata endpoints


        **Size limits**:

        - Images: 5 MB

        - Audio/Video: 16 MB

        - Documents: 100 MB


        Requests exceeding these limits fail immediately.
      operationId: uploadWhatsappMedia
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MediaIngestRequest'
            examples:
              meta_media:
                summary: Standard media upload
                value:
                  media_ingest:
                    phone_number_id: '713452918527238'
                    source: >-
                      https://upload.wikimedia.org/wikipedia/commons/2/2f/Example.png
                    delivery: meta_media
              meta_resumable_asset:
                summary: Resumable upload for profile picture
                value:
                  media_ingest:
                    phone_number_id: '713452918527238'
                    source: https://cdn.acme.com/profile.jpg
                    delivery: meta_resumable_asset
              with_hints:
                summary: Upload with optional filename and MIME hints
                value:
                  media_ingest:
                    phone_number_id: '713452918527238'
                    source: https://cdn.acme.com/documents/invoice-2025-01.pdf
                    delivery: meta_media
                    filename: Invoice-January-2025.pdf
                    mime_type: application/pdf
              minimal:
                summary: Minimal request (defaults to meta_media)
                value:
                  media_ingest:
                    phone_number_id: '713452918527238'
                    source: https://cdn.acme.com/photo.jpg
      responses:
        '200':
          description: Media uploaded successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MediaIngestResponse'
              examples:
                meta_media_success:
                  summary: Standard upload successful
                  value:
                    data:
                      ingest_id: 8a9b0c1d-2e3f-4a5b-6c7d-8e9f0a1b2c3d
                      target:
                        kind: meta_media
                        media_id: '1234567890123456'
                      resource:
                        filename: Example.png
                        mime_type: image/png
                        size_bytes: 2335
                        sha256: >-
                          69da8b7d9c0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6
                        source_url: >-
                          https://upload.wikimedia.org/wikipedia/commons/2/2f/Example.png
                meta_resumable_asset_success:
                  summary: Resumable upload successful
                  value:
                    data:
                      ingest_id: 5f6a7b8c-9d0e-1f2a-3b4c-5d6e7f8a9b0c
                      target:
                        kind: meta_resumable_asset
                        handle: h:resumable-upload-handle-abc123xyz789
                      resource:
                        filename: profile.jpg
                        mime_type: image/jpeg
                        size_bytes: 45678
                        sha256: >-
                          3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a
                        source_url: https://cdn.acme.com/profile.jpg
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                invalid_mime:
                  summary: Unsupported MIME type
                  value:
                    error: >-
                      Invalid MIME type: application/x-msdownload not supported
                      by Meta
                malformed_url:
                  summary: Invalid source URL
                  value:
                    error: Source URL must be a valid HTTP or HTTPS URL
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                sandbox_rejected:
                  summary: Sandbox configs not allowed
                  value:
                    error: Sandbox WhatsApp configurations cannot upload media
                private_ip_blocked:
                  summary: SSRF protection triggered
                  value:
                    error: Cannot fetch from private IP addresses or localhost
        '404':
          description: Phone number not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                not_found:
                  value:
                    error: >-
                      WhatsApp phone number not found or not accessible in this
                      project
        '413':
          description: Payload too large
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                size_exceeded:
                  summary: File exceeds size limit
                  value:
                    error: >-
                      File size 6291456 bytes exceeds limit of 5242880 bytes for
                      image/png
        '422':
          $ref: '#/components/responses/ValidationError'
        '429':
          description: Rate limit exceeded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                rate_limited:
                  value:
                    error: Meta API rate limit exceeded. Retry after 60 seconds
        '502':
          description: Meta API error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                meta_error:
                  summary: Meta rejected the upload
                  value:
                    error: 'Meta API error: Invalid OAuth access token'
components:
  schemas:
    MediaIngestRequest:
      type: object
      required:
        - media_ingest
      properties:
        media_ingest:
          $ref: '#/components/schemas/MediaIngest'
    MediaIngestResponse:
      type: object
      required:
        - data
      properties:
        data:
          $ref: '#/components/schemas/MediaIngestData'
    ErrorResponse:
      type: object
      required:
        - error
      properties:
        error:
          type: string
    MediaIngest:
      type: object
      required:
        - phone_number_id
        - source
      properties:
        phone_number_id:
          type: string
          description: Meta phone number ID
        source:
          type: string
          format: uri
          description: HTTP/HTTPS URL to media file
        delivery:
          type: string
          enum:
            - meta_media
            - meta_resumable_asset
          default: meta_media
          description: |
            Upload method:
            - meta_media: Standard upload (30-day lifetime)
            - meta_resumable_asset: Resumable upload (profile pictures, etc.)
        filename:
          type: string
          description: Optional filename hint
        mime_type:
          type: string
          description: Optional MIME type hint
    MediaIngestData:
      type: object
      required:
        - ingest_id
        - target
        - resource
      properties:
        ingest_id:
          type: string
          format: uuid
          description: Unique ingest request ID
        target:
          $ref: '#/components/schemas/MediaIngestTarget'
        resource:
          $ref: '#/components/schemas/MediaIngestResource'
    MediaIngestTarget:
      type: object
      required:
        - kind
      properties:
        kind:
          type: string
          enum:
            - meta_media
            - meta_resumable_asset
          description: Upload method used
        media_id:
          type: string
          description: Meta media ID (present when kind=meta_media)
        handle:
          type: string
          description: Resumable upload handle (present when kind=meta_resumable_asset)
    MediaIngestResource:
      type: object
      required:
        - filename
        - mime_type
        - size_bytes
        - sha256
        - source_url
      properties:
        filename:
          type: string
          description: Detected or provided filename
        mime_type:
          type: string
          description: Detected or provided MIME type
        size_bytes:
          type: integer
          minimum: 0
          description: File size in bytes
        sha256:
          type: string
          description: SHA256 checksum
        source_url:
          type: string
          format: uri
          description: Original source URL
  responses:
    ValidationError:
      description: Request validation failed
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key

````