Ethora Push API
The Push API from Ethora — 9 operation(s) for push.
The Push API from Ethora — 9 operation(s) for push.
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-push-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 Push 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: Push
paths:
/v1/push/firebase-service-account/{appId}:
post:
summary: Upload Firebase service account for app push
description: Uploads a Firebase service account JSON for the specified app. The backend proxies it to the configured push service.
tags:
- Push
parameters:
- in: path
name: appId
description: 'Target App ID. Must be an app the calling actor owns or has been
delegated access to.
'
schema:
type: string
required: true
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required:
- firebaseServiceAccount
properties:
firebaseServiceAccount:
type: string
format: binary
responses:
'200':
description: ok
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
description: forbidden
'404':
description: app not found
'422':
$ref: '#/components/responses/UnprocessableEntity'
'500':
$ref: '#/components/responses/InternalServerError'
operationId: post_v1_push_firebase_service_account_appid
delete:
summary: Delete the Firebase service account for app push
description: 'Removes the app''s Firebase service account from the push service and
clears the `firebaseServiceAccountUploaded` flag on the app. Idempotent:
succeeds even if no service account was uploaded. Device subscriptions
are left intact.'
tags:
- Push
parameters:
- in: path
name: appId
description: 'Target App ID. Must be an app the calling actor owns or has been
delegated access to.
'
schema:
type: string
required: true
responses:
'200':
description: ok
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
description: forbidden
'404':
description: app not found
'422':
$ref: '#/components/responses/UnprocessableEntity'
'500':
$ref: '#/components/responses/InternalServerError'
operationId: delete_v1_push_firebase_service_account_appid
/v1/push/apns/{appId}:
post:
summary: Upload the APNs auth key (.p8) for app push
description: 'Uploads an APNs auth key for direct APNs delivery (e.g. VoIP/PushKit
pushes, which FCM cannot deliver). The backend proxies it to the push
service, which stores it server-side — the key is never exposed back.
Sets `apnsKeyUploaded: true` on the app. `bundleId` defaults to the
app''s `bundleId` when omitted.'
tags:
- Push
parameters:
- in: path
name: appId
description: 'Target App ID. Must be an app the calling actor owns or has been
delegated access to.
'
schema:
type: string
required: true
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required:
- apnsKey
- keyId
- teamId
- environment
properties:
apnsKey:
type: string
format: binary
description: The APNs auth key file (.p8, PEM private key)
keyId:
type: string
description: Apple Key ID of the .p8 key (10 characters)
teamId:
type: string
description: Apple Developer Team ID (10 characters)
bundleId:
type: string
description: 'Target app bundle id (the apns-topic). May be omitted only
when the app record has `bundleId` set - the resolved value
is required, otherwise 422 BUNDLE_ID_REQUIRED.
'
environment:
type: string
enum:
- production
- sandbox
description: APNs environment the key will be used against
responses:
'200':
description: ok
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
description: forbidden
'404':
description: app not found
'422':
description: validation failed (missing/invalid keyId, teamId, environment, bundleId, or .p8 file)
'500':
$ref: '#/components/responses/InternalServerError'
operationId: post_v1_push_apns_appid
delete:
summary: Delete the APNs auth key for app push
description: 'Removes the app''s APNs auth key from the push service and clears the
`apnsKeyUploaded` flag on the app. Idempotent: succeeds even if no key
was uploaded.'
tags:
- Push
parameters:
- in: path
name: appId
description: 'Target App ID. Must be an app the calling actor owns or has been
delegated access to.
'
schema:
type: string
required: true
responses:
'200':
description: ok
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
description: forbidden
'404':
description: app not found
'422':
$ref: '#/components/responses/UnprocessableEntity'
'500':
$ref: '#/components/responses/InternalServerError'
operationId: delete_v1_push_apns_appid
/v1/push/subscription/{appId}:
post:
summary: Register a device push token for the current user
description: 'Creates or updates a push subscription for the current authenticated user (jid = req.user.xmppUsername).
Proxies to the configured push service.'
tags:
- Push
parameters:
- in: path
name: appId
description: 'Target App ID. Must be an app the calling actor owns or has been
delegated access to.
'
schema:
type: string
required: true
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- registrationToken
- deviceType
properties:
registrationToken:
type: string
description: FCM registration token, or a raw APNs/PushKit device token when tokenType is apns/apns-voip
tokenType:
type: string
enum:
- fcm
- apns
- apns-voip
description: '''fcm'' (default) - Firebase registration token, delivered via FCM.
''apns'' - raw APNs device token, delivered via direct APNs
(requires the app''s APNs auth key to be uploaded).
''apns-voip'' - PushKit VoIP token; registered alongside the
device''s regular ''apns'' subscription and used ONLY for
incoming-call pushes (apns-push-type voip, topic
bundleId.voip). Regular messages never go to it.
'
buildOrigin:
type: string
enum:
- tenant
- platform
description: 'Which build family issued the token — delivery keys must
match the BUILD that produced it, not the tenant.
''tenant'' (default) - the tenant''s own white-label build;
delivered with the keys uploaded for this app.
''platform'' - the universal store app; delivered with the
platform''s keys regardless of tenant.
'
deviceType:
type: string
enum:
- android
- ios
- web
language:
type: string
description: 'Language of the pushes to this device (BCP-47, e.g. en, fr,
es; the region part is ignored). The fixed texts (generic
mode, "New message", attachment words, reaction texts) and
the pick of a translated message text follow it; without
it the user''s profile language is used, then English.
Available: en, fr, es. Absent keeps the stored value, ""
clears it.
'
pushKey:
type: string
description: 'Encrypted pushes: base64 of the device''s raw 32-byte X25519
public key. Chat pushes to this token then arrive data-only
with their content in a libsodium sealed box for this key
(see the push service README, "Encrypted pushes"). An empty
string removes a stored key; omitting the field keeps it.
'
pushKeyAlg:
type: string
enum:
- sealed-box-x25519
description: Construction the key is for (default sealed-box-x25519).
pushKeyId:
type: string
description: 'Key id returned in every sealed push as `kid`. Default: the
first 16 hex characters of SHA-256 over the raw key.
'
responses:
'200':
description: ok
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'422':
description: validation error
'500':
$ref: '#/components/responses/InternalServerError'
operationId: post_v1_push_subscription_appid
delete:
summary: Remove a device push token for the current user
description: 'Deletes a push subscription for the current authenticated user (jid = req.user.xmppUsername).
Proxies to the configured push service.'
tags:
- Push
parameters:
- in: path
name: appId
description: 'Target App ID. Must be an app the calling actor owns or has been
delegated access to.
'
schema:
type: string
required: true
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- registrationToken
properties:
registrationToken:
type: string
responses:
'200':
description: ok
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'422':
description: validation error
'500':
$ref: '#/components/responses/InternalServerError'
operationId: delete_v1_push_subscription_appid
/v1/push/platform/{appId}:
put:
summary: Enable/disable platform-key push delivery for an app
description: 'Push keys uploaded to the base app serve every app of the install, with
no limits and no settings: an app without keys of its own sends with
the base app''s keys, an app that uploads its own keys sends with those,
and parent/child relations between apps play no role. This switch only
turns platform-key delivery off for one app (enabled: false) or back
on; it can also be set when the app is created (`platformPushEnabled`
on POST /v1/apps and /v2/apps). Accepts a user token with
appSettings.update permission on the target app, or a server (B2B)
token of that app or of its parent app.'
tags:
- Push
parameters:
- in: path
name: appId
schema:
type: string
required: true
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- enabled
properties:
enabled:
type: boolean
responses:
'200':
description: ok ({ ok, enabled })
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'422':
description: validation error
'500':
$ref: '#/components/responses/InternalServerError'
operationId: put_v1_push_platform_appid
get:
summary: Platform push status for an app
description: Returns { enabled, platformProjectId } for the app's platform-key delivery (platformProjectId is the app whose keys serve it, normally the base app).
tags:
- Push
parameters:
- in: path
name: appId
schema:
type: string
required: true
responses:
'200':
description: ok ({ enabled, platformProjectId })
'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'
operationId: get_v1_push_platform_appid
/v1/push/content-mode/{appId}:
put:
summary: Set what the chat pushes of an app say (full or generic)
description: '`full` (the default) is the usual chat push: the sender as the title,
the text or the attachment type as the body, the sender''s avatar or
the attachment preview as the picture. `generic` sends a fixed
"New message" / "You have a new message" with ids only in the data
payload (room JID, message id, receiver JID): no text, no names, no
pictures and no mutable-content, so nothing about the message passes
through Google or Apple; on Android the notification is sent with
visibility public. One notification per room and high priority apply
to both modes. Applies to chat messages only: call pushes and the
text pushes of /v1/push/user and /v1/push/project keep their text.
An app without a value of its own takes the mode of the nearest
parent app that has one, so one call on the base app covers its
child apps; `inherit` removes the app''s own value.
Accepts a user token with appSettings.update permission on the
target app, or a server (B2B) token of that app or of its parent app.'
tags:
- Push
parameters:
- in: path
name: appId
schema:
type: string
required: true
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- mode
properties:
mode:
type: string
enum:
- full
- generic
- inherit
responses:
'200':
description: ok ({ ok, mode })
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'422':
description: validation error
'500':
$ref: '#/components/responses/InternalServerError'
operationId: put_v1_push_content_mode_appid
get:
summary: Content mode of an app's chat pushes
description: Returns { mode, own, inheritedFrom }, the mode in effect ("full" or "generic"), the app's own value or null, and the app the mode is inherited from or null.
tags:
- Push
parameters:
- in: path
name: appId
schema:
type: string
required: true
responses:
'200':
description: ok ({ mode, own, inheritedFrom })
'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'
operationId: get_v1_push_content_mode_appid
/v1/push/sender-avatar/{appId}:
put:
summary: Switch the sender's avatar in an app's chat pushes on or off
description: 'On (the default), a chat push carries the sender''s avatar (the
`photo` of the message, else the sender''s profile image). The app
shows it the way messengers do: the avatar next to the text with
the app icon as a small badge (iOS communication notification,
Android MessagingStyle), where the app draws the notification
itself. Off leaves the avatar out of every chat push of the app,
plain and encrypted; attachment previews stay. The `generic`
content mode sends no avatar either way. Accepts a user token with
appSettings.update permission on the target app, or a server (B2B)
token of that app or of its parent app.'
tags:
- Push
parameters:
- in: path
name: appId
schema:
type: string
required: true
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- enabled
properties:
enabled:
type: boolean
responses:
'200':
description: ok ({ ok, enabled })
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'422':
description: validation error
'500':
$ref: '#/components/responses/InternalServerError'
operationId: put_v1_push_sender_avatar_appid
get:
summary: Whether an app's chat pushes carry the sender's avatar
description: Returns { enabled }, true unless switched off.
tags:
- Push
parameters:
- in: path
name: appId
schema:
type: string
required: true
responses:
'200':
description: ok ({ enabled })
'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'
operationId: get_v1_push_sender_avatar_appid
/v1/push/project/{appId}:
post:
summary: Send a broadcast push for an app (project)
description: Proxies a broadcast push request to the configured push service.
tags:
- Push
parameters:
- in: path
name: appId
description: 'Target App ID. Must be an app the calling actor owns or has been
delegated access to.
'
schema:
type: string
required: true
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- text
properties:
text:
type: string
title:
type: string
ttl:
type: integer
minimum: 0
jid:
type: string
description: Optional JID to target a specific segment/user group (push service dependent)
responses:
'200':
description: ok
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'422':
description: validation error
'500':
$ref: '#/components/responses/InternalServerError'
operationId: post_v1_push_project_appid
/v1/push/validate/{appId}:
post:
summary: Validate push configuration for an app (dry-run)
description: 'Calls the push service to perform a **dry-run** Firebase send to a topic.
This validates that the Firebase service account for the app is configured and usable,
without requiring a mobile app/device token.'
tags:
- Push
parameters:
- in: path
name: appId
description: 'Target App ID. Must be an app the calling actor owns or has been
delegated access to.
'
schema:
type: string
required: true
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
topic:
type: string
description: Optional FCM topic to use for dry-run validation.
responses:
'200':
description: ok
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
description: app/project not found
'422':
description: validation error
'500':
$ref: '#/components/responses/InternalServerError'
'502':
description: FCM validation failed (bad service account / permissions)
operationId: post_v1_push_validate_appid
/v1/push/user/{appId}:
post:
summary: Send a push to a single user (jid)
description: Proxies a single-user push request to the configured push service.
tags:
- Push
parameters:
- in: path
name: appId
description: 'Target App ID. Must be an app the calling actor owns or has been
delegated access to.
'
schema:
type: string
required: true
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- jid
- text
properties:
jid:
type: string
text:
type: string
title:
type: string
ttl:
type: integer
minimum: 0
responses:
'200':
description: ok
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'422':
description: validation error
'500':
$ref: '#/components/responses/InternalServerError'
operationId: post_v1_push_user_appid
components:
responses:
NotFound:
description: Not Found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
UnprocessableEntity:
description: Unprocessable Entity
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'
BadRequest:
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
schemas:
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.'