Endpoints
All public traffic enters through the gateway, which strips the prefix and forwards to a container. The path a controller sees is the upstream column, not the public one.
Public paths
| Public path | Upstream | Container |
|---|---|---|
/ | / (not stripped) | website |
/webview/ | / | webview SPA |
/admin/ | / | admin SPA |
/api/customer/ | / | customer API |
/api/admin/ | / | admin API |
/stream/ | / | multimedia |
/trailler/:id | handled by the website | website |
Auth legend: P public · U Firebase ID token · U? optional token · E existence check only · S provider secret · A admin JWT · SA super admin.
Customer API
Mounted at /, reached publicly under /api/customer/.
| Area | Endpoints | Method | Auth |
|---|---|---|---|
| Index | /, /version, /health | GET | P |
| Auth | /signup, /login | POST | P |
| Users | /users/verifySession/:userId, /users/verifySubscriptionToken, /users/:id | GET | P |
| Users | /users/updateUserPhoneNumber, /users/verifySubcription, /users/createPlaylist, /users/addVideoToPlayList, /users/deletePlaylist, /users/deleteVideoInsidePlaylist, /users/addUserDevice, /users/updateUserPoint, /users/updateUserLocation, /users/updateFcmToken, /users/updateUserPayPhoneNumber | PATCH | P |
| Videos | /videos/getActiveVideos, /videos/season/:seasonId, /videos/series/:seriesId, /videos/series/:seriesId/season/:seasonNumber, /videos/:id | GET | P |
| Videos | /videos/getArrayOfVideos | POST | P |
| Feedback | /videosFeedback/addVideoFeedBack | PATCH | P |
| Pay | /pay/stripe/webhook, /pay/webhook | POST | S |
| Pay | /pay/payByMobileMoney | POST | E |
| Pay | /pay/stripe/create-checkout-session, /create-embedded-checkout-session, /portal-session, /cancel, /change-plan | POST | E |
| Subscriptions | /subscriptions/getEnableSubscription | GET | P |
| Series | /series, /series/active, /series/search, /series/category/:category, /series/status/:status, /series/:id/seasons, /series/:id | GET | P |
| Seasons | /seasons/active, /seasons/:id/episodes, /seasons/:id, /seasons/series/:seriesId, /seasons/series/:seriesId/active, /seasons/series/:seriesId/season/:seasonNumber | GET | P |
| Search | /search | GET | P |
| Catalog | /catalog/home, /catalog/recommendations | GET | U? |
| Catalog | /catalog/top10, /catalog/browse | GET | P |
| Me | /me, /me/my-list, /me/continue-watching, /me/subscription | GET | U |
| Me | /me/my-list/:videoId | PUT, DELETE | U |
| Me | /me/views | POST | U |
| Me | /me/views/:videoId/progress | PATCH | U |
/series is not the public listThe website deliberately calls /series/active. /series also returns unpublished documents.
The same rule applies to any new catalogue surface.
/me* is the only strongly authenticated surface. Everything marked E trusts a
client-supplied user ID after an existence check; see Security notes.
Admin API
Mounted at /, reached publicly under /api/admin/.
| Area | Endpoints | Method | Auth |
|---|---|---|---|
| Index | /, /health | GET | P |
| Admin | /admin/login | POST | P |
| Admin | /admin/logout, /admin/admins, /admin/admins/:id, /admin/change-password | — | A |
| Admin | /admin/signup | POST | A + SA |
| Admin | /admin/admins/:id | DELETE | A + SA |
| Admin | /admin/audit-logs | GET | A + SA |
| Users | /users, /users/:id | GET | A |
| Videos | /videos, /videos/getVideoById/:id, /videos/getArrayOfVideos, /videos/:id/deletionPreview, /videos/standalone | GET | A |
| Videos | /videos/addVideo, /videos/saveVideo | POST | A |
| Videos | /videos, /videos/initThumbnailUpdate, /videos/confirmThumbnailUpdate | PUT | A |
| Videos | /videos/:id, /videos (body id) | DELETE | A |
| Uploads | /videos/signUpload, /videos/completeUpload | POST | A + rate limit |
| Uploads | /videos/uploadStatus | GET | A |
| Uploads | /videos/retryArtwork | POST | A |
| Notifications | /notifications | GET | A |
| Notifications | /notifications/stream | GET (SSE) | A via cookie |
| Feedback | /videosFeedback/getVideoFeedBack | GET | A |
| Pay | /pay/payByMobileMoney | POST | DTO + existence |
| Pay | /pay/webhook | POST | verif-hash |
| Subscriptions | /subscriptions, /subscriptions/getEnableSubscription | GET | A |
| Subscriptions | /subscriptions/createSubscription | POST | A |
| Subscriptions | /subscriptions/updateSubScriptionDefaultPrice, /subscriptions/:id | PUT | A |
| Subscriptions | /subscriptions/:id/toggle | PATCH | A |
| Subscriptions | /subscriptions/:id | DELETE | A |
| Series | /series, /series/active, /series/search, /series/category/:category, /series/status/:status, /series/:id | GET | A |
| Series | /series/create, /series/save | POST | A |
| Series | /series/initThumbnailUpdate, /series/confirmThumbnailUpdate, /series | PUT | A |
| Series | /series/:id/status, /series/:id/activate | PATCH | A |
| Series | /series (body id) | DELETE | A |
| Seasons | /seasons/series/:seriesId, /seasons/series/:seriesId/active, /seasons/series/:seriesId/season/:seasonNumber, /seasons/:id | GET | A |
| Seasons | /seasons/create, /seasons/save | POST | A |
| Seasons | /seasons | PUT | A |
| Seasons | /seasons/:id/activate | PATCH | A |
| Seasons | /seasons (body id) | DELETE | A |
| Episodes | /episodes/series/:seriesId, /episodes/season/:seasonId, /episodes/series/:seriesId/season/:seasonNumber/episode/:episodeNumber, /episodes/:episodeId | GET | A |
| Episodes | /episodes/convert | POST | A |
| Episodes | /episodes/:episodeId/convert-to-standalone, /episodes/move | PATCH | A |
| Episodes | /episodes/:episodeId, /episodes | DELETE | A |
| Dashboard | /dashboard/overview, /dashboard/videos, /dashboard/processing, /dashboard/users, /dashboard/payments | GET | A |
/videos/uploadStatus and /videos/retryArtwork are static paths. They must be registered
before any /videos/:id/... route, or the :id parameter shadows them and the request
reaches the wrong controller.
Upload kinds
signUpload accepts eight kinds:
video, trailer, thumbnail, thumbnailPreview, series-thumbnail, series-trailer,
season-thumbnail, season-trailer.
Images get a V4 signed PUT; video and trailer get a resumable session URI. The extension is
recorded at signing time so an object name never ends in .undefined.
Multimedia
| Path | Purpose |
|---|---|
/health | Liveness |
/api/cache/* | Cache statistics and eviction (unauthenticated) |
/videos/*, /trailers/* | HLS playlists and segments |
| catch-all | Proxied to node-media-server |
RTMP is on :1935, NMS HTTP on :8080, Express on :8000. Publish, play and cache stats are
unauthenticated and CORS is *.
Health
| Service | Probe | Body |
|---|---|---|
| Both APIs | GET /health | {status, appEnv, production[, version]} |
| Static frontends | GET /healthz | small text |
| Multimedia | GET /health | small JSON |
A body with only {status} is an older build, not a different contract.
Next: Branch strategy · Security notes.