Ethora Files API

Managing user uploaded files. ℹ️ Used for chat attachments, documents, user profiles, room media, digital collectibles, etc.

GitHub OpenAPI

Operations 9

POST /v1/files/ Upload files #
GET /v1/files/ List files #
POST /v2/files/ Upload files (v2) #
GET /v2/files/ List files (v2) #
POST /v2/files/secure Create secure #
GET /v1/files/{id} Get file #
DELETE /v1/files/{id} Delete file #
GET /v2/files/{id} Get file (v2) #
DELETE /v2/files/{id} Delete file (v2) #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/ethora-com:ethora-com-files-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

ethora-com-files-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Platform Files API
  description: 'Ethora Platform API.


    ## Getting Started

    - For new backend and tenant integrations, start with the `/v2/...` endpoints.

    - Use `/v2/apps/{appId}/...` when your backend/admin flow manages a specific target app explicitly.

    - Some legacy operations still exist only under `/v1/...`; when so, the operation description will say so.


    ## Integration Tracks

    - `Frontend / app runtime`: app-auth login/sign-up flows, then user-auth chats/files/sharelinks.

    - `Tenant admin / B2B backend`: explicit app management and app-scoped admin flows under `/v2/apps/{appId}/...`.


    ## Auth Token Legend

    - `App JWT`: app-scoped runtime token for sign-up/login/runtime app operations.

    - `User JWT`: end-user or admin-user session token.

    - `Refresh JWT`: refresh-only token for access token renewal.

    - `Client JWT`: legacy client-side token used only for chat/client credential exchange compatibility flows.

    - `B2B Server JWT`: tenant/backend integration token. Prefer `Authorization: Bearer `; `x-custom-token` is still accepted for backward compatibility.


    ## Which Token Where

    - `App JWT`: `/v1/users/login-with-email`, `/v2/users/sign-up-with-email`, similar app-runtime auth endpoints.

    - `User JWT`: user session endpoints like chats, files, sharelinks, profile/session management.

    - `B2B Server JWT`: tenant/backend integration endpoints, especially explicit `/v2/apps/{appId}/...` admin flows.

    - `Client JWT`: only where the endpoint explicitly says `Client JWT` and typically only for legacy compatibility.


    ## Auth Approach

    - `/v2/...` endpoints are the canonical API surface for new integrations.

    - Endpoints may support one or more token types depending on the actor and scope.

    - App-scoped admin/integration endpoints use `/v2/apps/{appId}/...` so the target app is always explicit.

    - When an endpoint supports both `User JWT` and `B2B Server JWT`, Swagger will show both under Security.

    - Public Swagger excludes internal-only, operational, and experimental surfaces.'
  contact:
    name: Dappros developers team
  version: 26.11.05
servers:
- url: https://api.chat.ethora.com
security:
- JwtAuth: []
tags:
- name: Files
  description: Managing user uploaded files. ℹ️ Used for chat attachments, documents, user profiles, room media, digital collectibles, etc.
paths:
  /v1/files/:
    post:
      description: '**Auth**: User JWT


        Files upload'
      tags:
      - Files
      operationId: v1_files_upload
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
              - files
              properties:
                files:
                  description: 'One or more files. The endpoint''s response lists each

                    file''s resulting metadata in the same order.

                    '
                  type: array
                  items:
                    type: string
                    format: binary
                clientEncrypted:
                  description: 'Set to `true` when the uploaded bytes are ciphertext the

                    client produced for an end-to-end encrypted chat. The

                    payload is then stored verbatim: no preview/thumbnail is

                    rendered, no duration or page-size probe runs, and

                    `mimetype` is recorded as declared rather than inferred.

                    Echoed back as `clientEncrypted` on each result so the

                    client knows to decrypt before rendering.

                    '
                  type: boolean
                  default: false
      responses:
        '200':
          description: "```\n{\n  success: boolean,\n  results: [{\n    userId: string,\n    ownerKey: string,\n    location: string,\n    locationPreview?: string,\n    duration?: string,\n    originalname: string,\n    filename: string,\n    mimetype: string,\n    size: string,\n    expiresAt: string,\n    clientEncrypted: boolean,  // bytes are client-side ciphertext\n    isPrivate: string,\n    _id: string,\n    createdAt: string,\n    updatedAt: string,\n  }],\n}\n```"
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FilesUploadResponse'
              examples:
                success:
                  value:
                    success: true
                    results:
                    - _id: 65f1a9c8e2b4c10012345678
                      userId: 65f1a9c8e2b4c10087654321
                      ownerKey: user
                      location: https://cdn.example.com/uploads/2026/01/file.jpg
                      locationPreview: https://cdn.example.com/uploads/2026/01/file_preview.jpg
                      originalname: file.jpg
                      filename: file_1700000000.jpg
                      mimetype: image/jpeg
                      size: '12345'
                      expiresAt: '2026-12-31T23:59:59.000Z'
                      isPrivate: 'false'
                      createdAt: '2026-01-01T12:00:00.000Z'
                      updatedAt: '2026-01-01T12:00:00.000Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '500':
          description: "Internal server error\n```\n {error: string}\n```"
      security:
      - UserJwtAuth: []
      summary: Upload files
    get:
      description: '**Auth**: User JWT


        get files created by user (JWT owner)'
      tags:
      - Files
      operationId: v1_files_list
      parameters:
      - in: query
        name: limit
        description: 'Maximum number of items to return per page. Server caps vary per

          endpoint; the response carries the actual page size.

          '
        schema:
          type: integer
          default: 10
          minimum: 1
          maximum: 500
      - in: query
        name: offset
        description: 'Number of items to skip from the beginning of the result set. Use

          with `limit` for paging.

          '
        schema:
          type: integer
          default: 0
          minimum: 0
      responses:
        '200':
          description: "```\n{\n  limit: number,\n  offset: number,\n  total: number,\n  items: [{\n    userId: string,\n    ownerKey: string,\n    location: string,\n    locationPreview?: string,\n    duration?: string,\n    originalname: string,\n    filename: string,\n    mimetype: string,\n    size: string,\n    expiresAt: string,\n    isPrivate: string,\n    _id: string,\n    createdAt: string,\n    updatedAt: string,\n  }],\n}\n```"
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FilesListResponse'
              examples:
                list:
                  value:
                    limit: 10
                    offset: 0
                    total: 1
                    items:
                    - _id: 65f1a9c8e2b4c10012345678
                      userId: 65f1a9c8e2b4c10087654321
                      ownerKey: user
                      location: https://cdn.example.com/uploads/2026/01/file.jpg
                      originalname: file.jpg
                      filename: file_1700000000.jpg
                      mimetype: image/jpeg
                      size: '12345'
                      expiresAt: '2026-12-31T23:59:59.000Z'
                      isPrivate: 'false'
                      createdAt: '2026-01-01T12:00:00.000Z'
                      updatedAt: '2026-01-01T12:00:00.000Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '500':
          $ref: '#/components/responses/InternalServerError'
      security:
      - UserJwtAuth: []
      summary: List files
  /v2/files/:
    post:
      description: '**Auth**: User JWT


        Files upload (v2)'
      tags:
      - Files
      operationId: v2_files_upload
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
              - files
              properties:
                files:
                  description: 'One or more files. The endpoint''s response lists each

                    file''s resulting metadata in the same order.

                    '
                  type: array
                  items:
                    type: string
                    format: binary
      responses:
        '200':
          description: "```\n{\n  success: boolean,\n  results: [{\n    userId: string,\n    ownerKey: string,\n    location: string,\n    locationPreview?: string,\n    duration?: string,\n    originalname: string,\n    filename: string,\n    mimetype: string,\n    size: string,\n    expiresAt: string,\n    isPrivate: string,\n    _id: string,\n    createdAt: string,\n    updatedAt: string,\n  }],\n}\n```"
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '500':
          description: "Internal server error\n```\n {error: string}\n```"
      security:
      - UserJwtAuth: []
      summary: Upload files (v2)
    get:
      description: '**Auth**: User JWT


        Get files created by user (JWT owner) (v2).

        v2 canonical: returns `{ results, pagination }`.

        For backward compatibility it also includes legacy-friendly keys `{ limit, offset, total, items }`.'
      tags:
      - Files
      operationId: v2_files_list
      parameters:
      - in: query
        name: limit
        description: 'Maximum number of items to return per page. Server caps vary per

          endpoint; the response carries the actual page size.

          '
        schema:
          type: integer
          default: 10
          minimum: 1
          maximum: 500
      - in: query
        name: offset
        description: 'Number of items to skip from the beginning of the result set. Use

          with `limit` for paging.

          '
        schema:
          type: integer
          default: 0
          minimum: 0
      responses:
        '200':
          description: "```\n{\n  results: [{\n    userId: string,\n    ownerKey: string,\n    location: string,\n    locationPreview?: string,\n    duration?: string,\n    originalname: string,\n    filename: string,\n    mimetype: string,\n    size: string,\n    expiresAt: string,\n    isPrivate: string,\n    _id: string,\n    createdAt: string,\n    updatedAt: string,\n  }],\n  pagination: { limit: number, offset: number, total: number },\n\n  // legacy-friendly keys (still present):\n  limit: number,\n  offset: number,\n  total: number,\n  items: array,\n}\n```"
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '500':
          $ref: '#/components/responses/InternalServerError'
      security:
      - UserJwtAuth: []
      summary: List files (v2)
  /v2/files/secure:
    post:
      description: '**Auth**: User JWT


        Secure chat-attachment upload (v2). Stores the file in the private

        secure bucket and binds it to a chat (`roomName`). Downloads are then

        gated per-fetch by chat membership and served from the secure files

        domain, so the returned `location` points at `secure-files.`

        (fetch it with the `fileToken` cookie on web, or `?ft=` on

        native/embedded clients). The caller must be a member of `chatName`.

        On a server without a secure files host the route answers

        `503 SECURE_FILES_DISABLED`; upload through `POST /v1/files` instead

        (the public files bucket).'
      tags:
      - Files
      operationId: v2_files_secure_upload
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
              - files
              - chatName
              properties:
                files:
                  description: One or more files to upload.
                  type: array
                  items:
                    type: string
                    format: binary
                chatName:
                  description: 'Internal chat name (`<appId>_<uuid>`) the attachment belongs

                    to. The uploader must be a member of this chat; the file is

                    bound to it and only members can fetch it afterwards.

                    '
                  type: string
                clientEncrypted:
                  description: 'Set to `true` when the uploaded bytes are ciphertext the

                    client produced for an end-to-end encrypted chat. The

                    payload is then stored verbatim: no preview/thumbnail is

                    rendered, no duration or page-size probe runs, and

                    `mimetype` is recorded as declared rather than inferred.

                    Echoed back as `clientEncrypted` on each result so the

                    client knows to decrypt before rendering.

                    '
                  type: boolean
                  default: false
      responses:
        '200':
          description: "```\n{\n  success: boolean,\n  results: [{\n    userId: string,\n    ownerKey: string,\n    location: string,          // https://secure-files.<domain>/<bucket>/<key>\n    locationPreview?: string,\n    originalname: string,\n    filename: string,\n    mimetype: string,\n    size: number,\n    objectKey: string,\n    roomName: string,          // the chat the file is bound to\n    clientEncrypted: boolean,  // bytes are client-side ciphertext\n    _id: string,\n    createdAt: string,\n    updatedAt: string,\n  }],\n}\n```"
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: The caller is not a member of `chatName`.
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          description: '`chatName` is missing, or no files were provided.'
        '500':
          description: Internal server error
      security:
      - UserJwtAuth: []
      summary: Create secure
  /v1/files/{id}:
    get:
      description: '**Deprecated**: use `/v2/files/{id}` instead.


        **Auth**: User JWT


        get file created by user (JWT owner) by id'
      tags:
      - Files
      operationId: v1_files_getById
      parameters:
      - in: path
        name: id
        description: 'ID of the target resource. The exact type (App, File, etc.)

          depends on the endpoint.

          '
        required: true
        schema:
          type: string
      responses:
        '200':
          description: "```\n{\n    userId: string,\n    ownerKey: string,\n    location: string,\n    locationPreview?: string,\n    duration?: string,\n    originalname: string,\n    filename: string,\n    mimetype: string,\n    size: string,\n    expiresAt: string,\n    isPrivate: string,\n    _id: string,\n    createdAt: string,\n    updatedAt: string,\n}\n```"
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FileRecord'
              examples:
                file:
                  value:
                    _id: 65f1a9c8e2b4c10012345678
                    userId: 65f1a9c8e2b4c10087654321
                    ownerKey: user
                    location: https://cdn.example.com/uploads/2026/01/file.jpg
                    originalname: file.jpg
                    filename: file_1700000000.jpg
                    mimetype: image/jpeg
                    size: '12345'
                    expiresAt: '2026-12-31T23:59:59.000Z'
                    isPrivate: 'false'
                    createdAt: '2026-01-01T12:00:00.000Z'
                    updatedAt: '2026-01-01T12:00:00.000Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Not found
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '500':
          description: Server side error
      security:
      - UserJwtAuth: []
      summary: Get file
      deprecated: true
    delete:
      description: '**Deprecated**: use `/v2/files/{id}` instead.


        **Auth**: User JWT


        delete created by user (JWT owner) by id'
      tags:
      - Files
      operationId: v1_files_deleteById
      parameters:
      - in: path
        name: id
        description: 'ID of the target resource. The exact type (App, File, etc.)

          depends on the endpoint.

          '
        required: true
        schema:
          type: string
      responses:
        '200':
          description: "```\n{\n    success: boolean,\n}\n```"
          content:
            application/json:
              schema:
                type: object
              examples:
                deleted:
                  value:
                    success: true
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Not found
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '500':
          description: Internal server error
      security:
      - UserJwtAuth: []
      summary: Delete file
      deprecated: true
  /v2/files/{id}:
    get:
      description: '**Auth**: User JWT


        get file created by user (JWT owner) by id (v2). Returns `{ result }`.'
      tags:
      - Files
      operationId: v2_files_getById
      parameters:
      - in: path
        name: id
        description: 'ID of the target resource. The exact type (App, File, etc.)

          depends on the endpoint.

          '
        required: true
        schema:
          type: string
      responses:
        '200':
          description: '```

            { result: { _id: string, ... }, success: boolean }

            ```'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Not found
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '500':
          description: Server side error
      security:
      - UserJwtAuth: []
      summary: Get file (v2)
    delete:
      description: '**Auth**: User JWT


        delete created by user (JWT owner) by id (v2)'
      tags:
      - Files
      operationId: v2_files_deleteById
      parameters:
      - in: path
        name: id
        description: 'ID of the target resource. The exact type (App, File, etc.)

          depends on the endpoint.

          '
        required: true
        schema:
          type: string
      responses:
        '200':
          description: "```\n{\n    success: boolean,\n}\n```"
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Not found
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '500':
          description: Internal server error
      security:
      - UserJwtAuth: []
      summary: Delete file (v2)
components:
  responses:
    UnprocessableEntity:
      description: Unprocessable Entity
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    NotFound:
      description: Not Found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    BadRequest:
      description: Bad Request
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    InternalServerError:
      description: Internal Server Error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Forbidden:
      description: Forbidden
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Unauthorized:
      description: Unauthorized
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
  schemas:
    FilesUploadResponse:
      type: object
      properties:
        success:
          type: boolean
        results:
          type: array
          items:
            $ref: '#/components/schemas/FileRecord'
      required:
      - success
      - results
    FileRecord:
      type: object
      properties:
        _id:
          type: string
        userId:
          type: string
        ownerKey:
          type: string
        location:
          type: string
        locationPreview:
          type:
          - string
          - 'null'
        duration:
          type:
          - string
          - 'null'
        originalname:
          type: string
        filename:
          type: string
        mimetype:
          type: string
        size:
          type: number
        expiresAt:
          type:
          - string
          - 'null'
        isPrivate:
          type:
          - boolean
          - 'null'
        createdAt:
          type:
          - string
          - 'null'
        updatedAt:
          type:
          - string
          - 'null'
      additionalProperties: true
    FilesListResponse:
      type: object
      properties:
        limit:
          type: integer
        offset:
          type: integer
        total:
          type: integer
        items:
          type: array
          items:
            $ref: '#/components/schemas/FileRecord'
      required:
      - limit
      - offset
      - total
      - items
    ErrorResponse:
      type: object
      properties:
        success:
          type: boolean
          description: Always false for error responses.
          example: false
        error:
          type: string
          description: A brief, human-readable message about the error.
        code:
          type: string
          description: A stable machine-readable error code.
          example: VALIDATION_ERROR
        details:
          type: object
          description: Optional, more specific details about the error (e.g., validation errors).
        errors:
          description: Legacy validation errors (if applicable).
        requestId:
          type: string
          description: Request correlation id for debugging/support.
      required:
      - error
      example:
        success: false
        error: Validation Failed
        code: VALIDATION_ERROR
        details:
          field: Reason
        requestId: '...'
  securitySchemes:
    JwtAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: 'Authorization: Bearer <JWT>'
    AppJwtAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: 'App JWT (data.type="app"). Use this token to call app-authorized endpoints (e.g. create user, login, signup). Authorization: Bearer <JWT>. Wrong token type returns code=INVALID_TOKEN_TYPE.'
    UserJwtAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: 'User JWT (data.type="user"). Use this token for user-authorized endpoints (e.g. chats/files/sharelinks/me). Authorization: Bearer <JWT>. Wrong token type returns code=INVALID_TOKEN_TYPE.'
    RefreshJwtAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: 'Refresh JWT (data.type="refresh"). Use this token only for refresh flow. Authorization: Bearer <JWT>. Wrong token type returns code=INVALID_TOKEN_TYPE.'
    ClientJwtAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: 'Client JWT (data.type="client"). Legacy compatibility token used only for client credential exchange endpoints. Preferred header: x-custom-token; Authorization Bearer may also be accepted by some deployments.'
    B2BServerJwtAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: 'B2B Server JWT (data.type="server"). Use this token for tenant/backend integration endpoints. Preferred header: Authorization: Bearer <JWT>. Legacy header x-custom-token is also accepted for backward compatibility.'