Platform
ScaiWave ScaiGrid ScaiCore ScaiBot ScaiDrive ScaiKey Models Tools & Services
Solutions
Organisations Developers Internet Service Providers Managed Service Providers AI-in-a-Box
Resources
Support Documentation Blog Downloads
Company
About Research Careers Investment Opportunities Contact
Log in

Sharing

Internal sharing: shares, members, and invitations. Everything that happens inside your tenant with people who have (or will have) accounts.

For sharing content with anonymous users via public URLs, see External Links.

Base path: /api/v1/shares/

Creating a share#

bash
1
2
3
4
5
6
7
8
curl -X POST $SCAIDRIVE_URL/api/v1/shares \
  -H "Authorization: Bearer $SCAIDRIVE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Q2 Planning",
    "share_type": "project",
    "owner_id": "grp_01J3L"
  }'
python
1
2
3
4
5
6
resp = httpx.post(
    f"{url}/api/v1/shares",
    headers={"Authorization": f"Bearer {token}"},
    json={"name": "Q2 Planning", "share_type": "project", "owner_id": "grp_01J3L"},
)
share = resp.json()
typescript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
const resp = await fetch(`${url}/api/v1/shares`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${token}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "Q2 Planning",
    share_type: "project",
    owner_id: "grp_01J3L",
  }),
});
const share = await resp.json();

Fields:

Field Notes
name (required) Display name
share_type (required) central, personal, or project
owner_id (required) usr_... or grp_... — who owns the share
description Optional
quota_bytes Optional; omit to inherit tenant quota

See Shares and Ownership for the types.

Listing your shares#

bash
1
2
curl -H "Authorization: Bearer $SCAIDRIVE_TOKEN" \
     $SCAIDRIVE_URL/api/v1/users/me/shares

Returns shares you're a member of (directly or via a group), with your role in each.

To list all shares in the tenant (admin view):

bash
1
2
curl -H "Authorization: Bearer $SCAIDRIVE_TOKEN" \
     $SCAIDRIVE_URL/api/v1/shares

Query parameters limit, offset, and include_trashed apply.

Searching for principals#

Before adding a member, look up the user or group:

bash
1
2
3
4
curl -G $SCAIDRIVE_URL/api/v1/shares/search/principals \
  -H "Authorization: Bearer $SCAIDRIVE_TOKEN" \
  --data-urlencode "q=alice" \
  --data-urlencode "principal_type=user"
json
1
2
3
4
5
6
{
  "users": [
    {"id": "usr_01J4M", "email": "alice@example.com", "name": "Alice Cooper"}
  ],
  "groups": []
}

Search hits email, name, and display_name. Omit principal_type to search both users and groups.

Adding a member#

bash
1
2
3
4
5
6
7
8
curl -X POST $SCAIDRIVE_URL/api/v1/shares/shr_01J3K/members \
  -H "Authorization: Bearer $SCAIDRIVE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "principal_type": "user",
    "principal_id": "usr_01J4M",
    "role": "contributor"
  }'

With time-bounded access:

json
1
2
3
4
5
6
{
  "principal_type": "user",
  "principal_id": "usr_01J4M",
  "role": "reader",
  "expires_at": "2026-07-01T00:00:00Z"
}

Requires MANAGE_PERMISSIONS on the share — effectively owner or admin role.

Role options: owner, admin, contributor, reader. See Shares and Ownership.

Listing members#

bash
1
2
curl -H "Authorization: Bearer $SCAIDRIVE_TOKEN" \
     $SCAIDRIVE_URL/api/v1/shares/shr_01J3K/members
json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
{
  "members": [
    {
      "principal_type": "group",
      "principal_id": "grp_01J3L",
      "principal_name": "Engineering",
      "role": "admin",
      "granted_by": "usr_01J3N",
      "expires_at": null
    },
    {
      "principal_type": "user",
      "principal_id": "usr_01J4M",
      "principal_name": "Alice Cooper",
      "role": "contributor",
      "granted_by": "usr_01J3N",
      "expires_at": "2026-07-01T00:00:00Z"
    }
  ],
  "total": 2
}

Changing a member's role#

bash
1
2
3
4
curl -X PATCH $SCAIDRIVE_URL/api/v1/shares/shr_01J3K/members/usr_01J4M \
  -H "Authorization: Bearer $SCAIDRIVE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"role": "admin"}'

You can also change expires_at.

Removing a member#

bash
1
2
curl -X DELETE $SCAIDRIVE_URL/api/v1/shares/shr_01J3K/members/usr_01J4M \
  -H "Authorization: Bearer $SCAIDRIVE_TOKEN"

Revoking group membership removes access for every user in that group (unless they have direct membership in addition).

Inviting someone who doesn't have an account#

For users not yet provisioned in ScaiKey, send an invitation:

bash
1
2
3
4
5
6
7
8
9
curl -X POST $SCAIDRIVE_URL/api/v1/shares/shr_01J3K/invite \
  -H "Authorization: Bearer $SCAIDRIVE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "partner@external.com",
    "role": "reader",
    "message": "Here are the specs we discussed.",
    "expires_in_days": 14
  }'

Response:

json
1
2
3
4
5
6
7
8
{
  "id": "inv_01J5N",
  "email": "partner@external.com",
  "role": "reader",
  "status": "pending",
  "token": "opaque-invitation-token",
  "expires_at": "2026-05-07T10:15:00Z"
}

An email is sent via ScaiSend with a link containing the token. When the recipient clicks:

  1. They authenticate via ScaiKey (creating an account if new).
  2. ScaiDrive provisions the user JIT.
  3. They call POST /api/v1/users/me/invitations/{invitation_id}/accept.
  4. A ShareMember record is created with the invitation's role.

Pending invitations for the calling user:

bash
1
2
curl -H "Authorization: Bearer $SCAIDRIVE_TOKEN" \
     $SCAIDRIVE_URL/api/v1/users/me/invitations

Accept or decline:

bash
1
2
3
4
5
curl -X POST $SCAIDRIVE_URL/api/v1/users/me/invitations/inv_01J5N/accept \
  -H "Authorization: Bearer $SCAIDRIVE_TOKEN"

curl -X POST $SCAIDRIVE_URL/api/v1/users/me/invitations/inv_01J5N/decline \
  -H "Authorization: Bearer $SCAIDRIVE_TOKEN"

Revoke a pending invitation:

bash
1
2
curl -X DELETE $SCAIDRIVE_URL/api/v1/shares/shr_01J3K/invitations/inv_01J5N \
  -H "Authorization: Bearer $SCAIDRIVE_TOKEN"

Listing pending invitations on a share#

bash
1
2
curl -H "Authorization: Bearer $SCAIDRIVE_TOKEN" \
     $SCAIDRIVE_URL/api/v1/shares/shr_01J3K/invitations

Returns invitations with status pending. Filter by status if you want the history — ?status=all.

Per-file and per-folder access#

Sometimes you want to grant one person access to one file within a share they're not a member of. For that, use the ACL API directly:

bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
curl -X POST $SCAIDRIVE_URL/api/v1/permissions/acl/file/fil_01J3M \
  -H "Authorization: Bearer $SCAIDRIVE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "principal_type": "user",
    "principal_id": "usr_01J4M",
    "permissions": ["READ"],
    "ace_type": "allow",
    "inherit_to_children": false
  }'

This doesn't make them a share member — they can access the file, but not see or list the share. See Permissions and ACLs.

For external parties without any ScaiDrive account, use External Links.

Updating the share itself#

bash
1
2
3
4
curl -X PATCH $SCAIDRIVE_URL/api/v1/shares/shr_01J3K \
  -H "Authorization: Bearer $SCAIDRIVE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Q2 Planning (archived)"}'

Requires MANAGE_PERMISSIONS. You can change name and description.

Deleting a share#

bash
1
2
curl -X DELETE $SCAIDRIVE_URL/api/v1/shares/shr_01J3K \
  -H "Authorization: Bearer $SCAIDRIVE_TOKEN"

Soft-deletes the share. Content is preserved for the tenant's trash-retention window. Permanent deletion requires tenant admin.

Common recipes#

Give a group read-only access#

python
1
2
3
4
5
httpx.post(
    f"{url}/api/v1/shares/{share_id}/members",
    headers={"Authorization": f"Bearer {token}"},
    json={"principal_type": "group", "principal_id": group_id, "role": "reader"},
)

Time-bounded contractor access#

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import datetime as dt

expires = (dt.datetime.now(dt.UTC) + dt.timedelta(days=90)).isoformat()
httpx.post(
    f"{url}/api/v1/shares/{share_id}/members",
    headers={"Authorization": f"Bearer {token}"},
    json={
        "principal_type": "user",
        "principal_id": contractor_id,
        "role": "contributor",
        "expires_at": expires,
    },
)

Invite several external users at once#

python
1
2
3
4
5
6
for email in ["a@ext.com", "b@ext.com", "c@ext.com"]:
    httpx.post(
        f"{url}/api/v1/shares/{share_id}/invite",
        headers={"Authorization": f"Bearer {token}"},
        json={"email": email, "role": "reader", "expires_in_days": 30},
    )

What's next#

Updated 2026-05-18 15:04:14 View source (.md) rev 2