Ethora Files API
Managing user uploaded files. ℹ️ Used for chat attachments, documents, user profiles, room media, digital collectibles, etc.
Managing user uploaded files. ℹ️ Used for chat attachments, documents, user profiles, room media, digital collectibles, etc.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
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.curl "https://apis.io/api/v1/apis/ethora-com:ethora-com-files-api"
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.
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: 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.'