{"info":{"name":"Vylo Storage","schema":"https://schema.getpostman.com/json/collection/v2.1.0/collection.json","description":"Set {{zone_key}} and {{zone}} in the collection variables first."},"variable":[{"key":"origin","value":"https://storage.vylo-tech.com"},{"key":"zone","value":"your-zone"},{"key":"zone_key","value":"vz_..."},{"key":"api_key","value":"vsk_..."}],"item":[{"name":"Files","item":[{"name":"Upload a file, or replace one","request":{"method":"PUT","header":[{"key":"Authorization","value":"Bearer {{zone_key}}"}],"url":{"raw":"{{origin}}/v1/{{zone}}/:key","host":["{{origin}}"],"path":["v1","{{zone}}",":key"],"variable":[{"key":"key","value":"","description":"the path within it, e.g. w1/intro.mp4"}]},"description":"Answers as soon as the bytes are safely on this server, not when they reach the storage box — that second hop is ours to worry about. Send Content-Type if you know it; it is what delivery will send back."}},{"name":"Fetch a file","request":{"method":"GET","header":[{"key":"Authorization","value":"Bearer {{zone_key}}"}],"url":{"raw":"{{origin}}/v1/{{zone}}/:key","host":["{{origin}}"],"path":["v1","{{zone}}",":key"],"variable":[{"key":"key","value":"","description":"key"}]},"description":"Supports range requests, ETag and 304, which is what lets a video player seek instead of downloading everything first. For a private zone the request needs a signature — see Links. Delivery is normally done straight from https://cdn.vylo-tech.com instead."}},{"name":"Size, type and ETag without the body","request":{"method":"HEAD","header":[{"key":"Authorization","value":"Bearer {{zone_key}}"}],"url":{"raw":"{{origin}}/v1/{{zone}}/:key","host":["{{origin}}"],"path":["v1","{{zone}}",":key"],"variable":[{"key":"key","value":"","description":"key"}]},"description":"Size, type and ETag without the body"}},{"name":"Delete one file","request":{"method":"DELETE","header":[{"key":"Authorization","value":"Bearer {{zone_key}}"}],"url":{"raw":"{{origin}}/v1/{{zone}}/:key","host":["{{origin}}"],"path":["v1","{{zone}}",":key"],"variable":[{"key":"key","value":"","description":"key"}]},"description":"Also removes any directory it was the last thing in."}},{"name":"List files, or one folder at a time","request":{"method":"GET","header":[{"key":"Authorization","value":"Bearer {{zone_key}}"}],"url":{"raw":"{{origin}}/v1/zones/{{zone}}/objects","host":["{{origin}}"],"path":["v1","zones","{{zone}}","objects"],"variable":[]},"description":"With delimiter=/ this answers the way object stores have since S3: the keys directly under prefix, plus the distinct next segments as folders, each carrying the count and bytes of everything beneath it."}},{"name":"Rename a file, or move it between folders","request":{"method":"POST","header":[{"key":"Authorization","value":"Bearer {{zone_key}}"},{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{origin}}/v1/zones/{{zone}}/move","host":["{{origin}}"],"path":["v1","zones","{{zone}}","move"],"variable":[]},"body":{"mode":"raw","raw":"{\n  \"source\": \"\",\n  \"destination\": \"\"\n}"},"description":"A rename on the storage box, so moving a 2 GB lecture does not move 2 GB. It will not overwrite something already there."}},{"name":"Delete a folder and everything under it","request":{"method":"DELETE","header":[{"key":"Authorization","value":"Bearer {{zone_key}}"}],"url":{"raw":"{{origin}}/v1/zones/{{zone}}/folder","host":["{{origin}}"],"path":["v1","zones","{{zone}}","folder"],"variable":[]},"description":"prefix is required and cannot be empty: deleting a folder and emptying an account must not be one keystroke apart."}}]},{"name":"Resumable uploads","item":[{"name":"Start a resumable upload","request":{"method":"POST","header":[{"key":"Authorization","value":"Bearer {{zone_key}}"}],"url":{"raw":"{{origin}}/v1/uploads","host":["{{origin}}"],"path":["v1","uploads"],"variable":[]},"description":"Upload-Metadata carries zone and key, each base64-encoded. The Location header is what you PATCH to."}},{"name":"Ask where an upload got to","request":{"method":"HEAD","header":[{"key":"Authorization","value":"Bearer {{zone_key}}"}],"url":{"raw":"{{origin}}/v1/uploads/:id","host":["{{origin}}"],"path":["v1","uploads",":id"],"variable":[{"key":"id","value":"","description":"id"}]},"description":"The one question resuming depends on. Answers Upload-Offset and Upload-Length."}},{"name":"Send the next stretch of bytes","request":{"method":"PATCH","header":[{"key":"Authorization","value":"Bearer {{zone_key}}"}],"url":{"raw":"{{origin}}/v1/uploads/:id","host":["{{origin}}"],"path":["v1","uploads",":id"],"variable":[{"key":"id","value":"","description":"id"}]},"description":"A wrong offset answers 409 carrying the right one, so a confused client can always recover. When the last byte lands, the response carries X-Vylo-Object with the delivery URL."}},{"name":"Give up on an upload","request":{"method":"DELETE","header":[{"key":"Authorization","value":"Bearer {{zone_key}}"}],"url":{"raw":"{{origin}}/v1/uploads/:id","host":["{{origin}}"],"path":["v1","uploads",":id"],"variable":[{"key":"id","value":"","description":"id"}]},"description":"Give up on an upload"}}]},{"name":"Encoding","item":[{"name":"What is inside a file, and whether a browser will play it","request":{"method":"GET","header":[{"key":"Authorization","value":"Bearer {{zone_key}}"}],"url":{"raw":"{{origin}}/v1/zones/{{zone}}/probe","host":["{{origin}}"],"path":["v1","zones","{{zone}}","probe"],"variable":[]},"description":"Worth asking before queueing anything. suggested_preset is null when the file is already fine, \"remux\" when the codecs are good but the container is not, and a size preset when it genuinely needs re-encoding."}},{"name":"What can be produced","request":{"method":"GET","header":[],"url":{"raw":"{{origin}}/v1/encodes/presets","host":["{{origin}}"],"path":["v1","encodes","presets"],"variable":[]},"description":"remux repackages without touching a pixel and runs at disk speed — it is the fix for an MKV or MOV that already holds H.264. The size presets re-encode and take real time."}},{"name":"Queue an encode","request":{"method":"POST","header":[{"key":"Authorization","value":"Bearer {{zone_key}}"},{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{origin}}/v1/encodes","host":["{{origin}}"],"path":["v1","encodes"],"variable":[]},"body":{"mode":"raw","raw":"{\n  \"zone\": \"{{zone}}\",\n  \"key\": \"\",\n  \"preset\": \"\"\n}"},"description":"Answers 202 straight away: encoding a lecture takes minutes and holding a request open for it is how integrations time out. The result lands beside the source — w1/lecture.mkv becomes w1/lecture.720p.mp4 — and the original is left alone."}},{"name":"How an encode is going","request":{"method":"GET","header":[{"key":"Authorization","value":"Bearer {{zone_key}}"}],"url":{"raw":"{{origin}}/v1/encodes/:id","host":["{{origin}}"],"path":["v1","encodes",":id"],"variable":[{"key":"id","value":"","description":"id"}]},"description":"state is queued, running, done or failed. progress is 0 to 1 while running."}},{"name":"Every encode for a zone, newest first","request":{"method":"GET","header":[{"key":"Authorization","value":"Bearer {{zone_key}}"}],"url":{"raw":"{{origin}}/v1/zones/{{zone}}/encodes","host":["{{origin}}"],"path":["v1","zones","{{zone}}","encodes"],"variable":[]},"description":"Every encode for a zone, newest first"}},{"name":"Cancel an encode","request":{"method":"DELETE","header":[{"key":"Authorization","value":"Bearer {{zone_key}}"}],"url":{"raw":"{{origin}}/v1/encodes/:id","host":["{{origin}}"],"path":["v1","encodes",":id"],"variable":[{"key":"id","value":"","description":"id"}]},"description":"A running job has its encoder stopped; a queued one is simply dropped."}}]},{"name":"Links","item":[{"name":"Mint a link that expires","request":{"method":"POST","header":[{"key":"Authorization","value":"Bearer {{zone_key}}"},{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{origin}}/v1/sign","host":["{{origin}}"],"path":["v1","sign"],"variable":[]},"body":{"mode":"raw","raw":"{\n  \"zone\": \"{{zone}}\",\n  \"key\": \"\",\n  \"ttl\": \"\",\n  \"viewer\": \"\",\n  \"ip\": \"\",\n  \"bind_ip\": \"\"\n}"},"description":"Unbound, a link is a bearer token: whoever holds it can watch until it expires. `viewer` makes it attributable — the id rides in the URL, is covered by the signature and cannot be edited, so a link that turns up in a group chat says who it was given to. `ip` goes further and stops it working anywhere else at all."}}]},{"name":"Embedding the player","item":[{"name":"The player page — this is what goes in the iframe","request":{"method":"GET","header":[{"key":"Authorization","value":"Bearer {{zone_key}}"}],"url":{"raw":"{{origin}}/play/{{zone}}/:key","host":["{{origin}}"],"path":["play","{{zone}}",":key"],"variable":[{"key":"key","value":"","description":"key"}]},"description":"Takes the exp and sig from a signed link. Public by design, because the signature IS the permission, and the one page that allows being framed on another site."}},{"name":"Everything a player needs, already signed","request":{"method":"GET","header":[{"key":"Authorization","value":"Bearer {{zone_key}}"}],"url":{"raw":"{{origin}}/v1/zones/{{zone}}/playable","host":["{{origin}}"],"path":["v1","zones","{{zone}}","playable"],"variable":[]},"description":"The renditions to offer as qualities, the poster and any caption tracks. Authenticated EITHER by a zone key or by the signature the viewer already holds — which is how the player page, holding no secret, builds a quality menu. Signatures it returns never outlive the one that asked for them."}}]},{"name":"The client library","item":[]},{"name":"Keeping a video private","item":[]},{"name":"Your account","item":[{"name":"Usage, quota and balance","request":{"method":"GET","header":[{"key":"Authorization","value":"Bearer {{api_key}}"}],"url":{"raw":"{{origin}}/me","host":["{{origin}}"],"path":["me"],"variable":[]},"description":"Usage, quota and balance"}},{"name":"A short-lived credential scoped to your zone","request":{"method":"POST","header":[{"key":"Authorization","value":"Bearer {{api_key}}"},{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{origin}}/account/upload-token","host":["{{origin}}"],"path":["account","upload-token"],"variable":[]},"body":{"mode":"raw","raw":"{\n  \"ttl\": \"\"\n}"},"description":"What a browser should hold instead of your zone key: it expires, and it works on nothing but your own zone. Anywhere these docs say $ZONE_KEY, one of these works too."}},{"name":"Replace your zone key","request":{"method":"POST","header":[{"key":"Authorization","value":"Bearer {{api_key}}"}],"url":{"raw":"{{origin}}/account/zone-key/rotate","host":["{{origin}}"],"path":["account","zone-key","rotate"],"variable":[]},"description":"Shown once. Anything still using the old one stops immediately."}},{"name":"Replace your API key","request":{"method":"POST","header":[{"key":"Authorization","value":"Bearer {{api_key}}"}],"url":{"raw":"{{origin}}/account/key/rotate","host":["{{origin}}"],"path":["account","key","rotate"],"variable":[]},"description":"Replace your API key"}},{"name":"What you have been billed, by month","request":{"method":"GET","header":[{"key":"Authorization","value":"Bearer {{api_key}}"}],"url":{"raw":"{{origin}}/account/charges","host":["{{origin}}"],"path":["account","charges"],"variable":[]},"description":"What you have been billed, by month"}},{"name":"Tell us you have paid","request":{"method":"POST","header":[{"key":"Authorization","value":"Bearer {{api_key}}"},{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{origin}}/account/payment-claims","host":["{{origin}}"],"path":["account","payment-claims"],"variable":[]},"body":{"mode":"raw","raw":"{\n  \"amount\": \"\",\n  \"method\": \"\",\n  \"reference\": \"\"\n}"},"description":"A claim never credits anything by itself; it is applied once it is confirmed."}}]}]}