SHOP DESKDevelopers
shopdeskbd.com

Videos

Product videos (H.264 MP4, up to 200 MB): upload one straight to storage (start a session, PUT the parts, complete) or import it from a link, set its cover, and place it on products — the server verifies every file.

Paste a key and every snippet on this page switches from the placeholder to your key — and the Try it panel under each endpoint is ready to send. It is stored in this browser only and goes nowhere except, if you press Send, straight to the API host you pick there.

Place a video on a product

POST/ext/v1/products/{id}/videosvideos:write

Puts one of your videos (from POST /videos or POST /videos/import) on the product: gallery_slot says how many photos come before it (default 1, right after the main photo), sort_order orders it among the product’s videos, option_value_id shows it only for one option value such as Colour: Red (a value one of the product’s active variants uses), and title names it. A video may be placed while it is still uploading or verifying; the product shows it (GET /products/{id} → videos[], the storefront) once it is ready, and a product.updated webhook is sent then. Placing a video the product already shows moves it instead. Nothing else on the product changes. The plan decides how many videos one product may show. A key made by rotating another may place that key’s videos too. Company-scoped keys must send store_id.

Path parameters

NameTypeDescription
idrequireduuidThe product id.

Headers

NameTypeDescription
Idempotency-KeystringOptional, but honored: send it to make retries of this request safe.

Body parameters

NameTypeDescription
video_idrequireduuidA video THIS API key started (POST /videos or POST /videos/import). It may still be uploading or verifying: the product shows it once it is ready.
gallery_slotnumberdefault 1How many of the product's photos come before the video: 0 = before every photo, 1 (default) = right after the main photo. Clamped to the number of photos.
sort_ordernumberdefault 0Order among the product's videos; breaks ties within one slot.
option_value_iduuidShow the video for one option value (e.g. Colour: Red — the value id from GET /attributes, or a variant’s options[].value_id). A value one of the product's active variants uses; null or absent = every variant.
titlestringIts accessible name. Empty or null falls back to the product name.
store_iduuidTarget store. REQUIRED with a company-scoped key; with a store-scoped key it may only repeat the key's own store.

Request

export SHOPDESK_API_KEY="shopdesk_sk_YOUR_API_KEY"

curl -sS -X POST "https://api.shopdeskbd.com/ext/v1/products/REPLACE_WITH_ID/videos" \
  -H "Authorization: Bearer $SHOPDESK_API_KEY" \
  -H "Idempotency-Key: 3f2b8c1e-9d4a-4e7b-8a6f-2c5d1e0b9a47" \
  -H "Content-Type: application/json" \
  -d '{
  "video_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "gallery_slot": 1,
  "option_value_id": "2c7e9f41-5b3a-4d8e-9f10-6a2b3c4d5e6f",
  "title": "Red saree drape"
}'
Try itruns from your browser
POSThttps://api.shopdeskbd.com/ext/v1/products/5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001/videos

Stored in this browser only and never sent anywhere except to the API host you picked above — these pages are static files with no server behind them.

Path parameters

Valid JSON
Idempotency-Keyoptional
generating…

A new value is generated whenever you edit this request, and kept while you do not — so sending twice without changing anything is a real retry and comes back Idempotent-Replayed: true instead of writing again.

Paste a key above to enable Send.

Responses

  • 200The placement, as saved.
  • 400An Idempotency-Key header was sent but is empty or longer than 255 characters (VALIDATION_ERROR; error.fields names the header).
  • 401Missing/invalid API key, revoked or expired key, plan without API access, or a dead store.
  • 403FORBIDDEN_SCOPE: the key's scopes do not cover this endpoint. PLAN_FEATURE_REQUIRED: the plan does not include product videos. PLAN_LIMIT_REACHED: the product already shows its plan's number of videos (or a released video would not fit the plan’s video storage again). VIDEO_UPLOADS_DISABLED: video uploads are switched off on this platform — no video can be placed (taking one off still works).
  • 404Product not found.
  • 409A request with this Idempotency-Key is still in flight (IDEMPOTENCY_IN_FLIGHT) — retry once it has finished.
  • 422Validation failed — error.fields names the field: video_id names no video this key started in this store, or one that was rejected or whose upload ended; option_value_id is used by none of the product’s active variants.
  • 429Rate limit exceeded for this key. Honor Retry-After and the X-RateLimit-* headers.

Every failure uses the one error envelope — Errors lists each code and what to do with it.

Example 200 response
{
  "product_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "video_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "status": "verifying",
  "gallery_slot": 1,
  "sort_order": 0,
  "option_value_id": null,
  "title": "Red saree drape"
}

Take a video off a product

DELETE/ext/v1/products/{id}/videos/{videoId}videos:write

The product stops showing the video; nothing else on it changes. The video itself stays — delete your own upload with DELETE /videos/{id} once no product shows it. Any video the product shows may be taken off, whoever uploaded it — even while video uploads are switched off. A company-scoped key names the store with the store_id query parameter, since a DELETE carries no body.

Path parameters

NameTypeDescription
idrequireduuidThe product id.
videoIdrequireduuidThe video id, as listed in the product’s videos[].

Query parameters

NameTypeDescription
store_iduuidRestrict the lookup to one store. Breadth control for company-scoped keys; on a store-scoped key it must match the key's own store.

Headers

NameTypeDescription
Idempotency-KeystringOptional, but honored: send it to make retries of this request safe.

Request

export SHOPDESK_API_KEY="shopdesk_sk_YOUR_API_KEY"

curl -sS -X DELETE "https://api.shopdeskbd.com/ext/v1/products/REPLACE_WITH_ID/videos/REPLACE_WITH_ID" \
  -H "Authorization: Bearer $SHOPDESK_API_KEY" \
  -H "Idempotency-Key: 3f2b8c1e-9d4a-4e7b-8a6f-2c5d1e0b9a47"
Try itruns from your browser
DELETEhttps://api.shopdeskbd.com/ext/v1/products/5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001/videos/6b1f3c2d-9e8a-4b7c-a6d5-1e2f3a4b5c6d

Stored in this browser only and never sent anywhere except to the API host you picked above — these pages are static files with no server behind them.

Path parameters

Query parameters

Blank fields are left out of the request.

Empty
Idempotency-Keyoptional
generating…

A new value is generated whenever you edit this request, and kept while you do not — so sending twice without changing anything is a real retry and comes back Idempotent-Replayed: true instead of writing again.

Paste a key above to enable Send.

Responses

  • 200The video is off the product.
  • 400An Idempotency-Key header was sent but is empty or longer than 255 characters (VALIDATION_ERROR; error.fields names the header).
  • 401Missing/invalid API key, revoked or expired key, plan without API access, or a dead store.
  • 403FORBIDDEN_SCOPE: the key's scopes do not cover this endpoint. PLAN_FEATURE_REQUIRED: the plan does not include product videos.
  • 404Product not found, or the product does not show the video.
  • 409A request with this Idempotency-Key is still in flight (IDEMPOTENCY_IN_FLIGHT) — retry once it has finished.
  • 422Validation failed — error.fields maps each offending field to its messages.
  • 429Rate limit exceeded for this key. Honor Retry-After and the X-RateLimit-* headers.

Every failure uses the one error envelope — Errors lists each code and what to do with it.

Example 200 response
{
  "product_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "video_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "removed": true
}

List your video uploads

GET/ext/v1/videosvideos:write

The uploads THIS API key started — and, for a key made by rotating another, the uploads of the keys it replaced — newest first, in every status unless status narrows it. Uploads made in the dashboard or by another key are not listed. An unfinished upload past its expiry is left out until it is recorded as aborted. uploaded_parts is empty in a list — read one upload for its parts.

Query parameters

NameTypeDescription
limitnumberdefault 25max 100Page size.
starting_afterstringReturn the page AFTER this object id (the previous page's next_cursor).
ending_beforestringReturn the page BEFORE this object id.
sortstringdefault -created_atNewest first by default. Only created_at is sortable — a cursor over a mutable key (like updated_at) cannot page reliably. Use updated_after to sync changes.created_at-created_at
store_idstringRestrict to one store. Required breadth control for company-scoped keys; on a store-scoped key it must match the key's own store.
statusstringOnly uploads in this status. Default: every status.uploadingverifyingreadyrejectedaborted

Request

export SHOPDESK_API_KEY="shopdesk_sk_YOUR_API_KEY"

curl -sS "https://api.shopdeskbd.com/ext/v1/videos?limit=25" \
  -H "Authorization: Bearer $SHOPDESK_API_KEY"
Try itruns from your browser
GEThttps://api.shopdeskbd.com/ext/v1/videos

Stored in this browser only and never sent anywhere except to the API host you picked above — these pages are static files with no server behind them.

Query parameters

Blank fields are left out of the request.

Paste a key above to enable Send.

Responses

  • 200A page of uploads.
  • 401Missing/invalid API key, revoked or expired key, plan without API access, or a dead store.
  • 403FORBIDDEN_SCOPE: the key's scopes do not cover this endpoint. PLAN_FEATURE_REQUIRED: the plan does not include product videos. VIDEO_UPLOADS_DISABLED: video uploads are switched off for now.
  • 422Validation failed — error.fields maps each offending field to its messages.
  • 429Rate limit exceeded for this key. Honor Retry-After and the X-RateLimit-* headers.

Every failure uses the one error envelope — Errors lists each code and what to do with it.

Example 200 response
{
  "data": [
    {
      "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
      "store_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
      "status": "uploading",
      "source": "upload",
      "file_name": "red-saree-drape.mp4",
      "size_bytes": 48000000,
      "part_size": 8388608,
      "part_count": 6,
      "checksum_algorithm": "SHA256",
      "uploaded_parts": [
        {
          "part_number": 1,
          "size": 8388608,
          "checksum_sha256": "n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg="
        }
      ],
      "uploaded_bytes": 16777216,
      "expires_at": "2026-08-11T10:00:00.000Z",
      "product_ids": [],
      "url": "https://cdn.example.com/companies/c/videos/v/video.mp4",
      "poster_url": null,
      "facts": {
        "duration_ms": 24000,
        "width": 1080,
        "height": 1920,
        "rotation": 0,
        "codecs": "avc1.640028, mp4a.40.2",
        "has_audio": true,
        "frame_rate": 29.97,
        "bitrate_kbps": 4200,
        "faststart": true
      },
      "warnings": [
        "NOT_FASTSTART"
      ],
      "failure": {
        "code": "VIDEO_HEVC",
        "message": "This video is HEVC (H.265).",
        "fix": "Export as H.264."
      },
      "created_at": "2026-08-11T10:00:00.000Z",
      "completed_at": "2026-08-11T10:00:00.000Z",
      "verified_at": "2026-08-11T10:00:00.000Z"
    }
  ],
  "meta": {
    "has_more": true,
    "next_cursor": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001"
  }
}

Start a video upload

POST/ext/v1/videosvideos:write

Opens an upload session for one H.264 MP4 of at most 200 MB (3 s to 5 min; AAC, MP3, Opus or no audio) and reserves its size against the plan's video storage. Then: ask POST /videos/{id}/part-urls for part URLs, PUT each part (exactly part_size bytes, the last one the rest) to its URL with the headers given, and call POST /videos/{id}/complete. Idempotency-Key is REQUIRED and is also the resume handle. A retry with the same key within 24 hours replays the first answer as it was (Idempotent-Replayed: true, resumed: false, its uploaded_parts as they were then); after that, the same key returns the same session with resumed: true. Either way the session is the same — read GET /videos/{id} for the parts storage holds now. Reuse a key only to retry the same file; once that session has ended (aborted), start over with a new key. A key made by rotating another keeps its uploads, and the same Idempotency-Key still names the same session. A finished video no product places within 48 hours is released (it stops counting against video storage) and removed 7 days later. At most 50 uploads start per store per day. Company-scoped keys must send store_id.

Headers

NameTypeDescription
Idempotency-KeyrequiredstringREQUIRED. A unique value per logical request (a UUID is ideal); reuse it verbatim when retrying. A retry replays the original response with Idempotent-Replayed: true instead of running twice.

Body parameters

NameTypeDescription
store_iduuidTarget store. REQUIRED with a company-scoped key; with a store-scoped key it may only repeat the key's own store.
file_namerequiredstringFor display only; never part of a storage key.
size_bytesrequirednumberThe exact size of the file in bytes — at most 200,000,000 (200 MB). It is reserved against the plan's video storage until the upload finishes or is cancelled.
part_sizerequirednumberBytes per part: 5, 8 or 16 MiB. Every part but the last is exactly this long; the answer says how many parts there are.5242880838860816777216
fingerprintstringOptional. Hex sha256 of <size_bytes>: followed by the sha256 of the file's first MiB and of its last MiB. When sent, starting again with the same file (under a new Idempotency-Key) resumes the unfinished upload instead of opening another.
product_iduuidThe product the video is meant for, for an early check of its video limit. It does not place the video.
client_probeobjectOptional. A client-side check of the file in the dashboard's ClientProbeReport shape (at most 32 KB as JSON); a file it already rejects is refused before any byte is sent. The server reads and verifies every file itself either way.

Request

export SHOPDESK_API_KEY="shopdesk_sk_YOUR_API_KEY"

curl -sS -X POST "https://api.shopdeskbd.com/ext/v1/videos" \
  -H "Authorization: Bearer $SHOPDESK_API_KEY" \
  -H "Idempotency-Key: 3f2b8c1e-9d4a-4e7b-8a6f-2c5d1e0b9a47" \
  -H "Content-Type: application/json" \
  -d '{
  "file_name": "red-saree-drape.mp4",
  "size_bytes": 48000000,
  "part_size": 8388608
}'
Try itruns from your browser
POSThttps://api.shopdeskbd.com/ext/v1/videos

Stored in this browser only and never sent anywhere except to the API host you picked above — these pages are static files with no server behind them.

Valid JSON
Idempotency-Keyrequired
generating…

A new value is generated whenever you edit this request, and kept while you do not — so sending twice without changing anything is a real retry and comes back Idempotent-Replayed: true instead of writing again.

Paste a key above to enable Send.

Responses

  • 201The upload session, with its part plan.
  • 400The required Idempotency-Key header is missing, empty, or longer than 255 characters (VALIDATION_ERROR; error.fields names the header).
  • 401Missing/invalid API key, revoked or expired key, plan without API access, or a dead store.
  • 403FORBIDDEN_SCOPE: the key's scopes do not cover this endpoint. PLAN_FEATURE_REQUIRED: the plan does not include product videos. VIDEO_UPLOADS_DISABLED: video uploads are switched off for now. PLAN_LIMIT_REACHED: the plan's video storage is full (or product_id’s product already has its plan’s number of videos).
  • 404product_id names no product of this store.
  • 409TOO_MANY_OPEN_UPLOADS: this key already has 4 unfinished uploads, or the store 6 — complete or delete one first; or a request with this Idempotency-Key is still in flight (IDEMPOTENCY_IN_FLIGHT) — retry once it has finished.
  • 422Validation failed — error.fields names the field. With client_probe: A file that breaks a rule gets its own code — VIDEO_NOT_MP4, VIDEO_HEVC, VIDEO_CODEC_UNSUPPORTED, VIDEO_PROFILE_UNSUPPORTED, VIDEO_AUDIO_UNSUPPORTED, VIDEO_FRAGMENTED, VIDEO_NO_VIDEO_TRACK, VIDEO_TOO_LARGE, VIDEO_TOO_LONG, VIDEO_TOO_SHORT, VIDEO_LOW_RES, VIDEO_FRAME_TOO_BIG, VIDEO_ROTATION_UNSUPPORTED or VIDEO_CORRUPT — with how to fix it in error.fields.video.
  • 429Rate limit exceeded for this key, or the store started 50 uploads today (RATE_LIMITED, Retry-After to the next UTC midnight).
  • 502STORAGE_UNAVAILABLE: video storage refused or was unreachable — retry in a moment.

Every failure uses the one error envelope — Errors lists each code and what to do with it.

Example 200 response
{
  "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "store_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "status": "uploading",
  "source": "upload",
  "file_name": "red-saree-drape.mp4",
  "size_bytes": 48000000,
  "part_size": 8388608,
  "part_count": 6,
  "checksum_algorithm": "SHA256",
  "uploaded_parts": [
    {
      "part_number": 1,
      "size": 8388608,
      "checksum_sha256": "n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg="
    }
  ],
  "uploaded_bytes": 16777216,
  "expires_at": "2026-08-11T10:00:00.000Z",
  "product_ids": [],
  "url": "https://cdn.example.com/companies/c/videos/v/video.mp4",
  "poster_url": null,
  "facts": {
    "duration_ms": 24000,
    "width": 1080,
    "height": 1920,
    "rotation": 0,
    "codecs": "avc1.640028, mp4a.40.2",
    "has_audio": true,
    "frame_rate": 29.97,
    "bitrate_kbps": 4200,
    "faststart": true
  },
  "warnings": [
    "NOT_FASTSTART"
  ],
  "failure": {
    "code": "VIDEO_HEVC",
    "message": "This video is HEVC (H.265).",
    "fix": "Export as H.264."
  },
  "created_at": "2026-08-11T10:00:00.000Z",
  "completed_at": "2026-08-11T10:00:00.000Z",
  "verified_at": "2026-08-11T10:00:00.000Z",
  "resumed": false
}

Get a video upload

GET/ext/v1/videos/{id}videos:write

The upload and its status. While uploading, uploaded_parts lists the parts storage already holds — resume by sending the rest. Poll this after a 202 from complete until ready or rejected. A finished video no product places within 48 hours is released and, 7 days later, removed — then this answers 404.

Path parameters

NameTypeDescription
idrequireduuidThe video id POST /videos returned.

Query parameters

NameTypeDescription
store_iduuidRestrict the lookup to one store. Breadth control for company-scoped keys; on a store-scoped key it must match the key's own store.

Request

export SHOPDESK_API_KEY="shopdesk_sk_YOUR_API_KEY"

curl -sS "https://api.shopdeskbd.com/ext/v1/videos/REPLACE_WITH_ID" \
  -H "Authorization: Bearer $SHOPDESK_API_KEY"
Try itruns from your browser
GEThttps://api.shopdeskbd.com/ext/v1/videos/6b1f3c2d-9e8a-4b7c-a6d5-1e2f3a4b5c6d

Stored in this browser only and never sent anywhere except to the API host you picked above — these pages are static files with no server behind them.

Path parameters

Query parameters

Blank fields are left out of the request.

Paste a key above to enable Send.

Responses

  • 200The upload.
  • 401Missing/invalid API key, revoked or expired key, plan without API access, or a dead store.
  • 403FORBIDDEN_SCOPE: the key's scopes do not cover this endpoint. PLAN_FEATURE_REQUIRED: the plan does not include product videos. VIDEO_UPLOADS_DISABLED: video uploads are switched off for now.
  • 404No video with this id was started by this API key (another key’s or a dashboard upload is not visible here).
  • 422Validation failed — error.fields maps each offending field to its messages.
  • 429Rate limit exceeded for this key. Honor Retry-After and the X-RateLimit-* headers.
  • 502STORAGE_UNAVAILABLE: video storage refused or was unreachable — retry in a moment.

Every failure uses the one error envelope — Errors lists each code and what to do with it.

Example 200 response
{
  "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "store_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "status": "uploading",
  "source": "upload",
  "file_name": "red-saree-drape.mp4",
  "size_bytes": 48000000,
  "part_size": 8388608,
  "part_count": 6,
  "checksum_algorithm": "SHA256",
  "uploaded_parts": [
    {
      "part_number": 1,
      "size": 8388608,
      "checksum_sha256": "n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg="
    }
  ],
  "uploaded_bytes": 16777216,
  "expires_at": "2026-08-11T10:00:00.000Z",
  "product_ids": [],
  "url": "https://cdn.example.com/companies/c/videos/v/video.mp4",
  "poster_url": null,
  "facts": {
    "duration_ms": 24000,
    "width": 1080,
    "height": 1920,
    "rotation": 0,
    "codecs": "avc1.640028, mp4a.40.2",
    "has_audio": true,
    "frame_rate": 29.97,
    "bitrate_kbps": 4200,
    "faststart": true
  },
  "warnings": [
    "NOT_FASTSTART"
  ],
  "failure": {
    "code": "VIDEO_HEVC",
    "message": "This video is HEVC (H.265).",
    "fix": "Export as H.264."
  },
  "created_at": "2026-08-11T10:00:00.000Z",
  "completed_at": "2026-08-11T10:00:00.000Z",
  "verified_at": "2026-08-11T10:00:00.000Z"
}

Delete a video upload

DELETE/ext/v1/videos/{id}videos:write

Cancels an unfinished upload (its reserved storage is released at once), or releases a finished video no live product shows: it stops counting against the plan's video storage now and is removed from storage after 7 days. A video only archived products show is taken off them. Deleting again changes nothing. A company-scoped key may narrow with the store_id query parameter.

Path parameters

NameTypeDescription
idrequireduuidThe video id POST /videos returned.

Query parameters

NameTypeDescription
store_iduuidRestrict the lookup to one store. Breadth control for company-scoped keys; on a store-scoped key it must match the key's own store.

Headers

NameTypeDescription
Idempotency-KeystringOptional, but honored: send it to make retries of this request safe.

Request

export SHOPDESK_API_KEY="shopdesk_sk_YOUR_API_KEY"

curl -sS -X DELETE "https://api.shopdeskbd.com/ext/v1/videos/REPLACE_WITH_ID" \
  -H "Authorization: Bearer $SHOPDESK_API_KEY" \
  -H "Idempotency-Key: 3f2b8c1e-9d4a-4e7b-8a6f-2c5d1e0b9a47"
Try itruns from your browser
DELETEhttps://api.shopdeskbd.com/ext/v1/videos/6b1f3c2d-9e8a-4b7c-a6d5-1e2f3a4b5c6d

Stored in this browser only and never sent anywhere except to the API host you picked above — these pages are static files with no server behind them.

Path parameters

Query parameters

Blank fields are left out of the request.

Empty
Idempotency-Keyoptional
generating…

A new value is generated whenever you edit this request, and kept while you do not — so sending twice without changing anything is a real retry and comes back Idempotent-Replayed: true instead of writing again.

Paste a key above to enable Send.

Responses

  • 200Deletion confirmed.
  • 400An Idempotency-Key header was sent but is empty or longer than 255 characters (VALIDATION_ERROR; error.fields names the header).
  • 401Missing/invalid API key, revoked or expired key, plan without API access, or a dead store.
  • 403FORBIDDEN_SCOPE: the key's scopes do not cover this endpoint. PLAN_FEATURE_REQUIRED: the plan does not include product videos. VIDEO_UPLOADS_DISABLED: video uploads are switched off for now.
  • 404No video with this id was started by this API key (another key’s or a dashboard upload is not visible here).
  • 409VIDEO_IN_USE: a live product shows the video — take it off the product first; UPLOAD_VERIFYING: the video is still being checked; or a request with this Idempotency-Key is still in flight (IDEMPOTENCY_IN_FLIGHT) — retry once it has finished.
  • 422Validation failed — error.fields maps each offending field to its messages.
  • 429Rate limit exceeded for this key. Honor Retry-After and the X-RateLimit-* headers.
  • 502STORAGE_UNAVAILABLE: video storage refused or was unreachable — retry in a moment.

Every failure uses the one error envelope — Errors lists each code and what to do with it.

Example 200 response
{
  "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "deleted": true
}

Complete a video upload

POST/ext/v1/videos/{id}/completevideos:write

Completes the file from the parts storage holds, then the server reads and checks it. 200 with status: ready when the verdict is in; 202 with status: verifying while the check is still running — poll GET /videos/{id}. Calling it again is safe: a ready video answers 200 again, a rejected one 422 again. Send no Idempotency-Key on a repeat meant to re-check a video that stayed verifying: a replay answers the first result.

Path parameters

NameTypeDescription
idrequireduuidThe video id POST /videos returned.

Headers

NameTypeDescription
Idempotency-KeystringOptional, but honored: send it to make retries of this request safe.

Request

export SHOPDESK_API_KEY="shopdesk_sk_YOUR_API_KEY"

curl -sS -X POST "https://api.shopdeskbd.com/ext/v1/videos/REPLACE_WITH_ID/complete" \
  -H "Authorization: Bearer $SHOPDESK_API_KEY" \
  -H "Idempotency-Key: 3f2b8c1e-9d4a-4e7b-8a6f-2c5d1e0b9a47"
Try itruns from your browser
POSThttps://api.shopdeskbd.com/ext/v1/videos/6b1f3c2d-9e8a-4b7c-a6d5-1e2f3a4b5c6d/complete

Stored in this browser only and never sent anywhere except to the API host you picked above — these pages are static files with no server behind them.

Path parameters

Empty
Idempotency-Keyoptional
generating…

A new value is generated whenever you edit this request, and kept while you do not — so sending twice without changing anything is a real retry and comes back Idempotent-Replayed: true instead of writing again.

Paste a key above to enable Send.

Responses

  • 200The video, ready.
  • 202The video, still verifying.
  • 400An Idempotency-Key header was sent but is empty or longer than 255 characters (VALIDATION_ERROR; error.fields names the header).
  • 401Missing/invalid API key, revoked or expired key, plan without API access, or a dead store.
  • 403FORBIDDEN_SCOPE: the key's scopes do not cover this endpoint. PLAN_FEATURE_REQUIRED: the plan does not include product videos. VIDEO_UPLOADS_DISABLED: video uploads are switched off for now.
  • 404No video with this id was started by this API key (another key’s or a dashboard upload is not visible here).
  • 409PARTS_INCOMPLETE: some parts are not in storage yet (error.fields names them) — upload them and complete again; FILE_CHANGED: a stored part does not match its checksum; or a request with this Idempotency-Key is still in flight (IDEMPOTENCY_IN_FLIGHT) — retry once it has finished.
  • 410UPLOAD_NOT_ACTIVE: the upload expired or was cancelled — start a new one.
  • 422A file that breaks a rule gets its own code — VIDEO_NOT_MP4, VIDEO_HEVC, VIDEO_CODEC_UNSUPPORTED, VIDEO_PROFILE_UNSUPPORTED, VIDEO_AUDIO_UNSUPPORTED, VIDEO_FRAGMENTED, VIDEO_NO_VIDEO_TRACK, VIDEO_TOO_LARGE, VIDEO_TOO_LONG, VIDEO_TOO_SHORT, VIDEO_LOW_RES, VIDEO_FRAME_TOO_BIG, VIDEO_ROTATION_UNSUPPORTED or VIDEO_CORRUPT — with how to fix it in error.fields.video. The file is deleted and its storage released.
  • 429Rate limit exceeded for this key. Honor Retry-After and the X-RateLimit-* headers.
  • 502STORAGE_UNAVAILABLE: video storage refused or was unreachable — retry in a moment.

Every failure uses the one error envelope — Errors lists each code and what to do with it.

Example 200 response
{
  "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "store_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "status": "uploading",
  "source": "upload",
  "file_name": "red-saree-drape.mp4",
  "size_bytes": 48000000,
  "part_size": 8388608,
  "part_count": 6,
  "checksum_algorithm": "SHA256",
  "uploaded_parts": [
    {
      "part_number": 1,
      "size": 8388608,
      "checksum_sha256": "n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg="
    }
  ],
  "uploaded_bytes": 16777216,
  "expires_at": "2026-08-11T10:00:00.000Z",
  "product_ids": [],
  "url": "https://cdn.example.com/companies/c/videos/v/video.mp4",
  "poster_url": null,
  "facts": {
    "duration_ms": 24000,
    "width": 1080,
    "height": 1920,
    "rotation": 0,
    "codecs": "avc1.640028, mp4a.40.2",
    "has_audio": true,
    "frame_rate": 29.97,
    "bitrate_kbps": 4200,
    "faststart": true
  },
  "warnings": [
    "NOT_FASTSTART"
  ],
  "failure": {
    "code": "VIDEO_HEVC",
    "message": "This video is HEVC (H.265).",
    "fix": "Export as H.264."
  },
  "created_at": "2026-08-11T10:00:00.000Z",
  "completed_at": "2026-08-11T10:00:00.000Z",
  "verified_at": "2026-08-11T10:00:00.000Z"
}

Get upload URLs for video parts

POST/ext/v1/videos/{id}/part-urlsvideos:write

Presigned PUT URLs for up to 10 parts, each valid for 60 minutes. Send each part’s base64 SHA-256: it is signed into the URL (storage refuses other bytes) and pinned — the same part asked for again with a different checksum is 409 FILE_CHANGED. Ask again for fresh URLs whenever they expire — and send no Idempotency-Key here: asking again is always safe, and a replayed answer would hand back the first URLs, which may have expired.

Path parameters

NameTypeDescription
idrequireduuidThe video id POST /videos returned.

Headers

NameTypeDescription
Idempotency-KeystringOptional, but honored: send it to make retries of this request safe.

Body parameters

NameTypeDescription
store_iduuidTarget store. REQUIRED with a company-scoped key; with a store-scoped key it may only repeat the key's own store.
partsrequiredVideoPartUrlRequestDto[]—

parts[] fields

NameTypeDescription
part_numberrequirednumber—
sha256requiredstringBase64 SHA-256 of the part's bytes. It is signed into the URL, and the first checksum sent for a part is pinned: asking again with a different one is 409 FILE_CHANGED.

Request

export SHOPDESK_API_KEY="shopdesk_sk_YOUR_API_KEY"

curl -sS -X POST "https://api.shopdeskbd.com/ext/v1/videos/REPLACE_WITH_ID/part-urls" \
  -H "Authorization: Bearer $SHOPDESK_API_KEY" \
  -H "Idempotency-Key: 3f2b8c1e-9d4a-4e7b-8a6f-2c5d1e0b9a47" \
  -H "Content-Type: application/json" \
  -d '{
  "parts": [
    {
      "part_number": 1,
      "sha256": "n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg="
    }
  ]
}'
Try itruns from your browser
POSThttps://api.shopdeskbd.com/ext/v1/videos/6b1f3c2d-9e8a-4b7c-a6d5-1e2f3a4b5c6d/part-urls

Stored in this browser only and never sent anywhere except to the API host you picked above — these pages are static files with no server behind them.

Path parameters

Valid JSON
Idempotency-Keyoptional
generating…

A new value is generated whenever you edit this request, and kept while you do not — so sending twice without changing anything is a real retry and comes back Idempotent-Replayed: true instead of writing again.

Paste a key above to enable Send.

Responses

  • 200One URL per part asked for.
  • 400An Idempotency-Key header was sent but is empty or longer than 255 characters (VALIDATION_ERROR; error.fields names the header).
  • 401Missing/invalid API key, revoked or expired key, plan without API access, or a dead store.
  • 403FORBIDDEN_SCOPE: the key's scopes do not cover this endpoint. PLAN_FEATURE_REQUIRED: the plan does not include product videos. VIDEO_UPLOADS_DISABLED: video uploads are switched off for now.
  • 404No video with this id was started by this API key (another key’s or a dashboard upload is not visible here).
  • 409FILE_CHANGED: a part was already signed with a different checksum — this is not the same file; start a new upload; or a request with this Idempotency-Key is still in flight (IDEMPOTENCY_IN_FLIGHT) — retry once it has finished.
  • 410UPLOAD_NOT_ACTIVE: the upload expired, was cancelled or already completed — start a new one.
  • 422Validation failed — error.fields maps each offending field to its messages.
  • 429Rate limit exceeded for this key. Honor Retry-After and the X-RateLimit-* headers.
  • 502STORAGE_UNAVAILABLE: video storage refused or was unreachable — retry in a moment.

Every failure uses the one error envelope — Errors lists each code and what to do with it.

Example 200 response
{
  "data": [
    {
      "part_number": 1,
      "url": "https://media.example.com/companies/c/videos/v/video.mp4?partNumber=1&uploadId=2a7f&X-Amz-Signature=9c1e",
      "headers": {
        "x-amz-checksum-sha256": "n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg="
      },
      "content_length": 8388608,
      "expires_at": "2026-08-11T10:00:00.000Z"
    }
  ]
}

Set a video cover

POST/ext/v1/videos/{id}/postervideos:write

Sets the image shown before the video plays; with none, product pages show the product thumbnail. Three sources: **upload** — multipart/form-data with the image in a file part and source as a text field; **product_image** — JSON { "source": "product_image", "image_id": "…" } with one of a product’s photos (copied beside the video, so deleting the photo later keeps the cover); **url** — JSON { "source": "url", "url": "https://…" }, which Shopdesk downloads and stores (the link is not kept). The image must be a JPEG, PNG or WebP of at most 2 MB, read from its bytes. A cover may be set while the video is still uploading, and replaced any time; a video a product shows gets its new cover on the product page at once. Send each NEW cover with a new Idempotency-Key, or none: a key is matched on the form fields, not the file’s bytes, so reusing one with a different file replays the first answer. The answer is the video with its poster_url.

Path parameters

NameTypeDescription
idrequireduuidThe video id POST /videos returned.

Headers

NameTypeDescription
Idempotency-KeystringOptional, but honored: send it to make retries of this request safe.

Body parameters

NameTypeDescription
store_iduuidTarget store. REQUIRED with a company-scoped key; with a store-scoped key it may only repeat the key's own store.
sourcerequiredstringupload: a JPEG, PNG or WebP of at most 2 MB in a multipart file part. product_image: one of the product photos (image_id), copied beside the video. url: an https link to the image, which Shopdesk downloads and stores (the link is not kept).uploadproduct_imageurl
image_iduuidproduct_image only: a photo of a product in this store, as listed in its images[].
urlstringurl only: an https link to a JPEG, PNG or WebP image.

Request

export SHOPDESK_API_KEY="shopdesk_sk_YOUR_API_KEY"

curl -sS -X POST "https://api.shopdeskbd.com/ext/v1/videos/REPLACE_WITH_ID/poster" \
  -H "Authorization: Bearer $SHOPDESK_API_KEY" \
  -H "Idempotency-Key: 3f2b8c1e-9d4a-4e7b-8a6f-2c5d1e0b9a47" \
  -H "Content-Type: application/json" \
  -d '{
  "source": "product_image",
  "image_id": "9c1e4b2a-7d3f-4e58-b0a1-2f6d8c9e1a45"
}'
Try itruns from your browser
POSThttps://api.shopdeskbd.com/ext/v1/videos/6b1f3c2d-9e8a-4b7c-a6d5-1e2f3a4b5c6d/poster

Stored in this browser only and never sent anywhere except to the API host you picked above — these pages are static files with no server behind them.

Path parameters

Valid JSON
Idempotency-Keyoptional
generating…

A new value is generated whenever you edit this request, and kept while you do not — so sending twice without changing anything is a real retry and comes back Idempotent-Replayed: true instead of writing again.

Paste a key above to enable Send.

Responses

  • 200The video, with its new poster_url.
  • 400An Idempotency-Key header was sent but is empty or longer than 255 characters (VALIDATION_ERROR; error.fields names the header).
  • 401Missing/invalid API key, revoked or expired key, plan without API access, or a dead store.
  • 403FORBIDDEN_SCOPE: the key's scopes do not cover this endpoint. PLAN_FEATURE_REQUIRED: the plan does not include product videos. VIDEO_UPLOADS_DISABLED: video uploads are switched off for now.
  • 404No video with this id was started by this API key (another key’s or a dashboard upload is not visible here).
  • 409A request with this Idempotency-Key is still in flight (IDEMPOTENCY_IN_FLIGHT) — retry once it has finished.
  • 410UPLOAD_NOT_ACTIVE: the upload was rejected, cancelled or expired — it takes no cover.
  • 413The file part is larger than 8 MB.
  • 422No usable image — error.fields names file, url or image_id: not a JPEG/PNG/WebP, over 2 MB, a link that cannot be downloaded, a photo id of no product in this store, or a missing or extra file part for the source.
  • 429Rate limit exceeded for this key. Honor Retry-After and the X-RateLimit-* headers.
  • 502STORAGE_UNAVAILABLE: video storage refused or was unreachable — retry in a moment.

Every failure uses the one error envelope — Errors lists each code and what to do with it.

Example 200 response
{
  "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "store_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "status": "uploading",
  "source": "upload",
  "file_name": "red-saree-drape.mp4",
  "size_bytes": 48000000,
  "part_size": 8388608,
  "part_count": 6,
  "checksum_algorithm": "SHA256",
  "uploaded_parts": [
    {
      "part_number": 1,
      "size": 8388608,
      "checksum_sha256": "n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg="
    }
  ],
  "uploaded_bytes": 16777216,
  "expires_at": "2026-08-11T10:00:00.000Z",
  "product_ids": [],
  "url": "https://cdn.example.com/companies/c/videos/v/video.mp4",
  "poster_url": null,
  "facts": {
    "duration_ms": 24000,
    "width": 1080,
    "height": 1920,
    "rotation": 0,
    "codecs": "avc1.640028, mp4a.40.2",
    "has_audio": true,
    "frame_rate": 29.97,
    "bitrate_kbps": 4200,
    "faststart": true
  },
  "warnings": [
    "NOT_FASTSTART"
  ],
  "failure": {
    "code": "VIDEO_HEVC",
    "message": "This video is HEVC (H.265).",
    "fix": "Export as H.264."
  },
  "created_at": "2026-08-11T10:00:00.000Z",
  "completed_at": "2026-08-11T10:00:00.000Z",
  "verified_at": "2026-08-11T10:00:00.000Z"
}

Import a video from a link

POST/ext/v1/videos/importvideos:write

Shopdesk downloads an H.264 MP4 from an https link to the file itself (not a player page such as YouTube) and stores its own copy — the link is not kept. Always answered 202 at once: the copy runs in the background, so poll GET /videos/{id} until ready or rejected (or aborted with an import_* failure code). The link must be public (so must any of the up to 3 redirects it takes) and must declare its size (Content-Length): an import is refused before its body is downloaded when that is over 200 MB or over the plan's remaining video storage, which is reserved from the start and released if the import fails; a download longer or shorter than declared is refused too. Nothing stays in storage from a failed import. The file is then verified exactly like an upload. Imports run one at a time per company; one whose copy has not started within an hour ends aborted (expired), and one whose link sends under 64 KB a second on average after its first minute ends import_download_failed. If the key is revoked or expires, or the plan loses product videos, before the copy starts, the import ends aborted (cancelled). Without file_name the video is named after the link’s last path segment when that reads as a file name (red-saree.mp4), else video.mp4. Idempotency-Key is REQUIRED and names the import: within 24 hours a retry replays the first answer; after that the same key returns the same video (resumed: true) — and a retry is never refused by the daily cap. At most 20 imports start per key per day, within the store’s 50 uploads a day. Company-scoped keys must send store_id.

Headers

NameTypeDescription
Idempotency-KeyrequiredstringREQUIRED. A unique value per logical request (a UUID is ideal); reuse it verbatim when retrying. A retry replays the original response with Idempotent-Replayed: true instead of running twice.

Body parameters

NameTypeDescription
store_iduuidTarget store. REQUIRED with a company-scoped key; with a store-scoped key it may only repeat the key's own store.
source_urlrequiredstringAn https link to the MP4 file itself (not a player page such as YouTube). Shopdesk downloads it and stores its own copy; the link is not kept.
file_namestringFor display only. Defaults to the link's last path segment.
product_iduuidThe product the video is meant for, for an early check of its video limit. It does not place the video — POST /products/{id}/videos does.

Request

export SHOPDESK_API_KEY="shopdesk_sk_YOUR_API_KEY"

curl -sS -X POST "https://api.shopdeskbd.com/ext/v1/videos/import" \
  -H "Authorization: Bearer $SHOPDESK_API_KEY" \
  -H "Idempotency-Key: 3f2b8c1e-9d4a-4e7b-8a6f-2c5d1e0b9a47" \
  -H "Content-Type: application/json" \
  -d '{
  "source_url": "https://cdn.example.com/videos/red-saree-drape.mp4"
}'
Try itruns from your browser
POSThttps://api.shopdeskbd.com/ext/v1/videos/import

Stored in this browser only and never sent anywhere except to the API host you picked above — these pages are static files with no server behind them.

Valid JSON
Idempotency-Keyrequired
generating…

A new value is generated whenever you edit this request, and kept while you do not — so sending twice without changing anything is a real retry and comes back Idempotent-Replayed: true instead of writing again.

Paste a key above to enable Send.

Responses

  • 202The video, source: import, still uploading while it is copied.
  • 400The required Idempotency-Key header is missing, empty, or longer than 255 characters (VALIDATION_ERROR; error.fields names the header).
  • 401Missing/invalid API key, revoked or expired key, plan without API access, or a dead store.
  • 403FORBIDDEN_SCOPE: the key's scopes do not cover this endpoint. PLAN_FEATURE_REQUIRED: the plan does not include product videos. VIDEO_UPLOADS_DISABLED: video uploads are switched off for now. PLAN_LIMIT_REACHED: the plan's video storage is full (or product_id’s product already has its plan’s number of videos).
  • 404product_id names no product of this store.
  • 409TOO_MANY_OPEN_UPLOADS: this key already has 4 unfinished uploads or imports, or the store 6 — wait for one to finish or delete one; or a request with this Idempotency-Key is still in flight (IDEMPOTENCY_IN_FLIGHT) — retry once it has finished.
  • 422Validation failed — error.fields names the field. source_url must be an https link without a username or password, on port 443 or from 1024 up, and not a video player page.
  • 429Rate limit exceeded for this key, or this key started 20 imports (or the store 50 uploads) today (RATE_LIMITED, Retry-After to the next UTC midnight).
  • 502STORAGE_UNAVAILABLE: video storage refused or was unreachable — retry in a moment.

Every failure uses the one error envelope — Errors lists each code and what to do with it.

Example 200 response
{
  "id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "store_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
  "status": "uploading",
  "source": "upload",
  "file_name": "red-saree-drape.mp4",
  "size_bytes": 48000000,
  "part_size": 8388608,
  "part_count": 6,
  "checksum_algorithm": "SHA256",
  "uploaded_parts": [
    {
      "part_number": 1,
      "size": 8388608,
      "checksum_sha256": "n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg="
    }
  ],
  "uploaded_bytes": 16777216,
  "expires_at": "2026-08-11T10:00:00.000Z",
  "product_ids": [],
  "url": "https://cdn.example.com/companies/c/videos/v/video.mp4",
  "poster_url": null,
  "facts": {
    "duration_ms": 24000,
    "width": 1080,
    "height": 1920,
    "rotation": 0,
    "codecs": "avc1.640028, mp4a.40.2",
    "has_audio": true,
    "frame_rate": 29.97,
    "bitrate_kbps": 4200,
    "faststart": true
  },
  "warnings": [
    "NOT_FASTSTART"
  ],
  "failure": {
    "code": "VIDEO_HEVC",
    "message": "This video is HEVC (H.265).",
    "fix": "Export as H.264."
  },
  "created_at": "2026-08-11T10:00:00.000Z",
  "completed_at": "2026-08-11T10:00:00.000Z",
  "verified_at": "2026-08-11T10:00:00.000Z",
  "resumed": false
}