Folder Sharing via API
Sharing opens access to a folder through a link. The recipient needs no Kinescope account and no workspace invitation — just the link and, if you set one, a password.
The /v1/shares endpoints do the same thing as the Share button in the dashboard: they create a link, set a password and an expiry date, rotate the link, and revoke access. This article shows how to call them from your code. Authorization, response format, and the general shape of errors are covered in the general API guidelines
.
When to create the link from code
- Your LMS or CRM grants access automatically. A student pays for a course, your backend creates a share for the course folder and emails the link.
- Access is temporary. A contractor works with the footage until the shoot ends: you set
expires_atand the link stops working on its own. - The link leaked. A single
reset-tokencall issues a new address and keeps every setting, while the old address stops working right away. - A subscription ended.
DELETErevokes the share and closes access for everyone you sent it to.
If at least one of these sounds familiar, read on.
How a share works
- You create a share for a folder and get back a share object:
share_idfor later calls, atoken, and a ready-madelink. - The link looks like
https://kinescope.io/sh/{token}. The token is 22 characters computed fromshare_id, so you don’t need to store it separately. - A folder can have only one active share. A second
POSTfor the same folder returns409. - You change the settings — password, dates, downloads, name — with
PATCH, and they take effect immediately. - Access closes in two ways:
reset-tokenchanges the link and keeps the settings,DELETErevokes the share for good.
Now let’s go through the calls one by one.
What you need before the first request
- A workspace API token. Pass it in the
Authorization: Bearer YOUR_API_TOKENheader. - A paid plan. On the free plan, creating a share returns
403with code403001. - The
entity_idof a folder. Only a folder can be shared. For a video or any other object, the API returns400with code400001.
password field comes back in plain text. Don’t pass the sharing response to the browser as is — use has_password when all you need is to show that a password is set.Creating a link to a folder
POST https://api.kinescope.io/v1/shares
curl -X POST 'https://api.kinescope.io/v1/shares' \
-H 'Authorization: Bearer YOUR_API_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"entity_id": "5b8a54a1-1f4f-4a5f-9a3a-2a4b0e2f1c77",
"display_name": "Python course — cohort 12",
"password": "python-2026",
"allow_download": true,
"expires_at": "2026-12-01T00:00:00Z"
}'
Request body fields:
| Field | Type | Required | Description |
|---|---|---|---|
entity_id | UUID | yes | The folder you open access to |
display_name | string | no | Share name, up to 255 characters |
password | string | no | Password for entry, at least 3 characters |
allow_download | boolean | no | Allow file downloads, false by default |
starts_at | RFC3339 | no | The moment the link starts working |
expires_at | RFC3339 | no | The moment the link stops working |
Dates are converted to UTC. expires_at must be in the future, and starts_at must be earlier than expires_at. Otherwise the API returns 422.
Response 200 OK:
{
"data": {
"share_id": "9f1d3c02-77a0-4f5e-8b26-1e0a5d3c8b41",
"entity_id": "5b8a54a1-1f4f-4a5f-9a3a-2a4b0e2f1c77",
"workspace_id": "0c1b7a94-3f2e-4d6c-9a11-77c1f2b3d4e5",
"display_name": "Python course — cohort 12",
"password": "python-2026",
"allow_download": true,
"is_active": true,
"starts_at": null,
"expires_at": "2026-12-01T00:00:00Z",
"created_at": "2026-08-31T12:38:38.475095Z",
"updated_at": null,
"token": "kDAcY15owhyW1RQaFq14rc",
"link": "https://kinescope.io/sh/kDAcY15owhyW1RQaFq14rc",
"has_password": true
}
}
Give the recipient the link and keep the share_id: every other call needs it.
Reading the settings of an existing share
If you have the share_id, request the share directly:
curl 'https://api.kinescope.io/v1/shares/9f1d3c02-77a0-4f5e-8b26-1e0a5d3c8b41' \
-H 'Authorization: Bearer YOUR_API_TOKEN'
If you only know the folder, look the share up by it:
curl 'https://api.kinescope.io/v1/shares/entity/5b8a54a1-1f4f-4a5f-9a3a-2a4b0e2f1c77' \
-H 'Authorization: Bearer YOUR_API_TOKEN'
Both requests return only an active share, and the same object as creation does. A revoked share is not found by share_id or by entity_id.
Changing the password, dates, and downloads
PATCH https://api.kinescope.io/v1/shares/{share_id}
Send only the fields you are changing. The link stays the same.
curl -X PATCH 'https://api.kinescope.io/v1/shares/9f1d3c02-77a0-4f5e-8b26-1e0a5d3c8b41' \
-H 'Authorization: Bearer YOUR_API_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"password": "",
"allow_download": false,
"expires_at": "2027-01-15T00:00:00Z"
}'
Fields have three states, and an empty value differs from a missing one:
| What you send | What happens |
|---|---|
| Field not sent | The value stays unchanged |
"password": "new-one" | The password is set or changed |
"password": "" | The password is removed and entry becomes free |
"display_name": "" | The custom name is removed |
"starts_at": null or "expires_at": null | The date restriction is removed |
"expires_at": "2027-01-15T00:00:00Z" | The date is set; it must be in the future |
The response is the full share object with the new values.
Rotating the link without losing settings
Did the link reach the wrong person? Rotate it:
curl -X POST 'https://api.kinescope.io/v1/shares/9f1d3c02-77a0-4f5e-8b26-1e0a5d3c8b41/reset-token' \
-H 'Authorization: Bearer YOUR_API_TOKEN'
Kinescope revokes the current share and creates a new one with the same password, dates, name, and download permission. The old link stops working right away.
share_id, token, and link. Replace the stored share_id — every following request with the old one returns 404.You can rotate the token only on an active share. On a revoked one, the call returns 409 with code 409297 — create a new share for the same folder instead.
Revoking access
DELETE https://api.kinescope.io/v1/shares/{share_id}
curl -X DELETE 'https://api.kinescope.io/v1/shares/9f1d3c02-77a0-4f5e-8b26-1e0a5d3c8b41' \
-H 'Authorization: Bearer YOUR_API_TOKEN'
Response 200 OK:
{
"data": {
"success": true
}
}
Revocation is final. To open access again, create a new share for the same folder — the link will be different.
Sharing errors
Errors arrive in the common format: an error object with a numeric code and a message.
| HTTP | error.code | When it happens |
|---|---|---|
| 400 | 400001 | entity_id points to something other than a folder |
| 403 | 100103 | The token has no rights to this folder |
| 403 | 403001 | Sharing is not available on the free plan |
| 409 | 409001 | The folder already has an active share |
| 409 | 409297 | The share is revoked, so reset-token is unavailable |
| 422 | 422001 | Parameters failed validation |
Example response to an attempt to share a video:
{
"error": {
"code": 400001,
"message": "only a folder can be shared"
}
}
Example response to an expires_at in the past:
{
"error": {
"code": 422001,
"message": "request parameters didn't validate"
}
}
When the error maps to a specific field — a password shorter than three characters, for example — the response also carries an invalid_params array with the field name and the reason.
If the resource is not found, the API responds like this:
HTTP/1.1 404 Not Found
Content-Type: application/json
{ "error" : { "code" : 400404, "message" : "not found" } }
Example: granting folder access from your backend
This function creates a share with an expiry date and returns the share_id and the ready-made link:
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"time"
)
type createShareRequest struct {
EntityID string `json:"entity_id"`
Password string `json:"password,omitempty"`
ExpiresAt *time.Time `json:"expires_at,omitempty"`
}
type shareResponse struct {
Data struct {
ShareID string `json:"share_id"`
Link string `json:"link"`
} `json:"data"`
}
// CreateShare opens access to folder folderID for the ttl period.
func CreateShare(apiToken, folderID, password string, ttl time.Duration) (string, string, error) {
expiresAt := time.Now().UTC().Add(ttl)
body, err := json.Marshal(createShareRequest{
EntityID: folderID,
Password: password,
ExpiresAt: &expiresAt,
})
if err != nil {
return "", "", err
}
req, err := http.NewRequest(http.MethodPost, "https://api.kinescope.io/v1/shares", bytes.NewReader(body))
if err != nil {
return "", "", err
}
req.Header.Set("Authorization", "Bearer "+apiToken)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", "", fmt.Errorf("kinescope: share not created, status %d", resp.StatusCode)
}
var out shareResponse
if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
return "", "", err
}
// You need share_id for PATCH, reset-token, and DELETE — store it.
return out.Data.ShareID, out.Data.Link, nil
}
What’s next?
- General API guidelines — authorization, response format, request limits
- File upload via API — fill the folder before you share it
- Authorization backend — access control for individual videos by your system’s rules
Still have questions? Write to the support chat within the Kinescope interface — our specialists will help!