JMV Public API

REST API for managing VOD videos, live events, playlists, streams and uploading files directly to S3/MinIO storage.

Version
v2 (official endpoint)
Authentication
Bearer JWT (HS256, 4h)
Format
application/json
Max upload
5 TB (S3 multipart)

Base URLs

Production https://api.jmvstream.com

Errors

The API uses standard HTTP status codes. Error responses return an object with a message key.

CodeMeaning
400Invalid or missing parameters
401Missing, expired or invalid token
404Resource not found
507No storage space available
500Internal server error
// Example error response
{
  "message": "not allowed, token is expired"
}

Authentication

POST /v2/authenticate Official endpoint

Generates a JWT token using only the plan UUID (resource). Official endpoint — no user credentials required.

Body

FieldTypeRequiredDescription
resource string (UUID) yes Customer plan UUID

Request

POST /v2/authenticate
Content-Type: application/json

{
  "resource": "eeba7244-e850-4064-aa61-008718b6e4b0"
}

Response — 200 OK

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Using the token

Include the token in every authenticated request via the Authorization header:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
ℹ️
Expiration Tokens expire after 4 hours. Once expired, the API returns 401 — not allowed, token is expired. Generate a new token with the authentication endpoint.

JWT payload (decoded)

{
  "planId":          123,
  "planUuid":        "eeba7244-e850-4064-aa61-008718b6e4b0",
  "planType":        "od",
  "planDescription": "VOD Enterprise Plan",
  "id_customers":    456,
  "sub": {
    "id_customers": 456,
    "email":        "admin@company.com"
  },
  "iat": 1750870000,
  "exp": 1750884400
}

Videos

All video routes require authentication via Bearer token.

GET /v1/videos/application Paginated list with filters

Returns the paginated list of videos in the plan, with filtering and sorting options.

Query Parameters

ParameterTypeDefaultDescription
pagenumber1Current page
perPagenumber10Items per page (max. 50)
sortASC | DESCASCSort direction
orderBystringnameSort field
namestringFilter by name (like)
initialDateYYYY-MM-DDFilter start date
endDateYYYY-MM-DDFilter end date
GET /v1/videos/:hash Single lookup

Returns a single video formatted with the plan secret for generating signed URLs.

Path Parameters

ParameterTypeDescription
hashstringUnique video hash (15 alphanumeric chars)
DELETE /v1/videos/deleteVideo/:hash/:customer_plans_id Delete video

Deletes a video from the plan. Deletion is processed asynchronously — the video becomes unavailable immediately, but physically removing the file may take a few moments to complete.

PUT /v1/videos/moveVideo/:hash Move to another gallery

Moves a video to a different gallery.

Body

FieldTypeRequiredDescription
gallerystring (UUID)yesDestination gallery UUID
PUT /v1/videos/editVideo/:hash/:planId Rename video

Updates a video's display name.

Body

FieldTypeRequiredDescription
namestringyesNew video name
PUT /v1/videos/uploadThumb/:hash/:planId Thumbnail upload

Uploads a thumbnail via multipart/form-data. Limited to 1 MB. The image is sent to the Static API and the video's thumb field is updated.

ℹ️
The request Content-Type must be multipart/form-data, not application/json.
GET /v1/videos/converting/:customerPlanId Videos in conversion

Returns the list of videos with conversion jobs in progress for the specified plan.

GET /v1/videos/job-status/:videoHash Conversion status

Returns the normalized status of a video's conversion job.

Return values

StatusCondition
COMPLETEDprogress ≥ 100
CONVERTINGstatus = converting or progress > 0
ERRORstatus = ERROR or FAILED
AWAIT_CONVERSIONall other cases

Galleries

GET /v1/folders List galleries with hierarchy

Returns the plan's gallery tree, including child galleries (children).

{
  "folders": [
    {
      "id":          1,
      "uuid":        "a1b2c3d4-...",
      "name":        "Training",
      "description": "Internal training videos",
      "children": []
    }
  ]
}
POST /v1/folders Create gallery

Body

FieldTypeRequiredDescription
namestringyesGallery name
descriptionstringnoDescription
parentstring (UUID)noParent gallery UUID (to create a subgallery)
PUT /v1/folders/:uuid Update gallery

Updates the name and/or description of an existing gallery.

DELETE /v1/folders/:uuid Delete gallery

Deletes a gallery. Galleries with associated videos cannot be deleted.


Live Events

POST /v1/events/insert Create event

Creates a new event with the initial status WAITING_LIVE.

Body

FieldTypeRequiredDescription
titlestringyesEvent title
scheduledDatestring (ISO 8601)yesScheduled date and time
GET /v1/events/searchAll List events

Returns a paginated list of the plan's events.

PUT /v1/events/start-or-end-live Control event status

Changes the status of a live event. The lifecycle follows the sequence:

WAITING_LIVE  →  LIVE  →  ON_DEMAND
DELETE /v1/events/delete Delete events

Deletes one or more events. Accepts an array of IDs in the body.

Body

{
  "ids": [42, 43, 44]
}

Playlist

Playlist management for AVJ plans. Lets you order and control the content displayed.

GET /v1/playlist/:customerplan List playlist

Returns all videos in a plan's playlist in the configured order.

POST /v1/playlist/:customerplan/add Add to playlist

Body

FieldTypeRequiredDescription
videoIdnumberyesID of the video to add
PUT /v1/playlist/:customerplan/move/:videoId Reorder video

Changes the position of a video in the playlist.

Body

FieldTypeRequiredDescription
positionnumberyesNew position (1-based)
DELETE /v1/playlist/:customerplan/remove Remove from playlist

Removes a video from the playlist without deleting it from the system.

GET /v1/playlist/:id_plan/playing Now playing

Returns the video currently playing in the player. Queries the Webtv database (real-time read).


Streams v2

Proxy to the Stream Gateway. All operations are forwarded to the internal service via X-API-KEY. The stream_id is generated automatically in the format OT-NORMAL-{name}-{stream_key}.

POST /v2/streams Create stream

Creates a new stream target on the Stream Gateway.

Body

FieldTypeRequiredDescription
namestringyesStream identifier name
stream_keystringyesUnique stream key
GET /v2/streams/:stream_id Real-time status

Returns the current broadcast status directly from the Stream Gateway.

PUT /v2/streams/:stream_id/control Enable / disable

Body

FieldTypeRequiredDescription
actionenable | disableyesAction to run on the broadcast
DELETE /v2/streams/:stream_id Delete stream

Removes the broadcast from the Stream Gateway. This operation is irreversible.


S3 Upload v2

Upload videos directly to MinIO (S3-compatible) using presigned URLs. File content never passes through the API — the client sends it straight to storage. Supports files up to 5 TB.

🔄
Recommended flow: 3 steps 1. POST /v2/upload/multipart/s3 → get presigned URLs
2. PUT each URL directly to MinIO (client-side) → collect ETags
3. POST /v2/upload/multipart/complete → finalize and process

Strategies by file size

SizeStrategyChunk Size
< 100 MBdirect_uploadFull file (1 part)
100 MB – 1 GBmedium_chunks16 MB
1 GB – 100 GBlarge_chunks64 MB
100 GB – 5 TBhuge_chunksDynamic (max. 10,000 parts)
POST /v2/upload/multipart/s3 Start multipart upload

Creates a multipart upload in MinIO and returns all presigned URLs at once. Each URL is valid for 6 hours.

Body

FieldTypeRequiredDescription
fileNamestringyesOriginal file name (with extension)
fileSizenumberyesFile size in bytes
totalPartsnumberyesTotal number of parts to send
chunkSizenumberyesSize of each chunk in bytes

Request

POST /v2/upload/multipart/s3
Authorization: Bearer <token>
Content-Type: application/json

{
  "fileName":   "training-video.mp4",
  "fileSize":   1073741824,
  "totalParts": 16,
  "chunkSize":  67108864
}

Response — 200 OK

{
  "uploadId":    "minio-upload-id-xyz",
  "objectName":  "uploads/vod123/a8f3k2m9x1q4p.mp4",
  "video_hash":  "a8f3k2m9x1q4p",
  "totalParts":  16,
  "presignedUrls": [
    { "partNumber": 1, "url": "https://minio.../upload?partNumber=1&uploadId=..." },
    { "partNumber": 2, "url": "https://minio.../upload?partNumber=2&uploadId=..." }
  ]
}

Sending the parts (client-side)

For each presigned URL, make a PUT directly to MinIO with the corresponding chunk:

// JavaScript — send each part
const uploadedParts = [];

for (let i = 0; i < presignedUrls.length; i++) {
  const { partNumber, url } = presignedUrls[i];
  const start = i * chunkSize;
  const chunk = file.slice(start, start + chunkSize);

  const res = await fetch(url, {
    method: 'PUT',
    body: chunk,
    headers: { 'Content-Type': 'application/octet-stream' }
  });

  uploadedParts.push({
    partNumber,
    etag: res.headers.get('ETag')
  });
}
POST /v2/upload/multipart/complete Complete multipart upload

Completes the multipart upload in MinIO, registers the video in the database and queues the processing job on the s3-upload-queue queue.

Body

FieldTypeRequiredDescription
filenamestringyesOriginal file name
sizenumberyesSize in bytes
video_hashstringyesHash returned by init
objectNamestringyesObject name returned by init
uploadIdstringyesUpload ID returned by init
partsarrayyesArray of { partNumber, etag } collected during upload
gallerystring (UUID)noDestination gallery UUID

Request

{
  "filename":   "training-video.mp4",
  "size":       1073741824,
  "video_hash": "a8f3k2m9x1q4p",
  "objectName": "uploads/vod123/a8f3k2m9x1q4p.mp4",
  "uploadId":   "minio-upload-id-xyz",
  "gallery":   "a1b2c3d4-e5f6-...",
  "parts": [
    { "partNumber": 1, "etag": "\"abc123\"" },
    { "partNumber": 2, "etag": "\"def456\"" }
  ]
}
POST /v2/upload/s3-direct Direct upload (single file)

Generates a presigned URL to upload a file in a single PUT request. Valid for 24 hours. Recommended for files smaller than 100 MB.

Body

FieldTypeRequiredDescription
filenamestringyesFile name with extension
fileSizenumberyesSize in bytes
gallerystring (UUID)noDestination gallery UUID
POST /v2/upload/s3-direct-complete Confirm direct upload

Confirms that the file was uploaded to MinIO via PUT, validates that the object exists (headObject), registers the video and queues the processing job.

Body

FieldTypeRequiredDescription
hashstringyesHash returned by the create endpoint
gallerystring (UUID)noDestination gallery UUID