> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stackshift.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# Library, spaces, collections and sharing

> Find and organize media, switch Assets spaces, invite collaborators with explicit permissions, and handle access changes safely.

## Goal

Organize a shared media library without confusing storage ownership, collection membership or project access.

## Prerequisites

* An authenticated StackShift account; accept any invitation using the invited verified email.
* An Assets space you own or have an explicit grant to use.

## Workflow

<Steps>
  <Step>
    Select the Assets space before browsing or uploading.
  </Step>

  <Step>
    Filter the library and organize readable assets into collections.
  </Step>

  <Step>
    Invite collaborators to the entire space or a specific collection.
  </Step>

  <Step>
    Recheck access and saved references when changing permissions.
  </Step>
</Steps>

## Choose the right space

Open Assets from the dashboard, then choose the space in the space selector. A space is the ownership and authorization boundary for the library; a bucket is a storage/policy grouping within it. Storage and media usage belong to the space owner, including work submitted by collaborators. Project membership alone does not grant Assets access.

Switching spaces resets the inspector, selected assets and upload context. Confirm the selected space before starting an upload. A missing bucket or empty list is not an invitation to recreate another space’s resources.

## Navigate the workspace

* Library: browse grid or list views, search, filter, inspect and organize assets. Videos display a poster instead of autoplaying in the grid.
* Review: find publication snapshots awaiting review, approval or publication. This is separate from malware scan status.
* Galleries: arrange published media into version-pinned sequences.
* Uploads: follow the upload queue. A completed upload can still need scanning or media processing.
* Manage → Metadata configures schemas and bucket governance; Manage → Access manages collaborator grants. Manage → Operations → Activity shows the existing asset event history.

## Search and filter media

Use the search box with bucket, folder/prefix, collection, media type, MIME type, visibility and security/status filters. Grid and list views share the same pagination. The URL retains the space, filters, view and cursor; sharing that URL shares a view, not permission to its contents.

For a governed bucket, fields explicitly marked searchable and approved caption/transcript metadata participate in the search projection. Unreviewed AI suggestions are not silently promoted to approved editorial tags. This is indexed text search, not a promise of visual similarity search.

Duplicates means matching SHA-256 content, not visually similar files. Duplicate filtering happens before pagination. A duplicate group can span pages; filter by its checksum to inspect the full group. Reloading a later cursor may return to the first page when earlier cursor history is no longer available.

## Collections and saved searches

A collection stores explicit membership. Creating, renaming or changing membership persists on the server; failed writes leave the form available to correct and retry. A saved search stores filter criteria and evaluates the current library each time; it is not a frozen collection.

Collection access does not allow a curator to add an asset they cannot already read. Deleting a collection removes the organizational record, not the asset files. Before changing membership, consider collaborators whose only access is through that collection.

```ts theme={null}
// Backend SDK instance scoped to the selected Assets space.
const collection = await sdk.assets.createCollection({
  name: 'Spring catalog', asset_ids: readableAssetIds,
})
const page = await sdk.assets.list({
  collectionId: collection.id, query: 'chair',
  type: 'image', sort: 'name', direction: 'asc', limit: 25,
})
const updated = await sdk.assets.updateCollection(
  collection.id, collection.revision,
  { name: 'Spring catalog — approved', asset_ids: readableAssetIds },
)
// Use a new read and its revision before a later edit.
```

## Invite and accept a collaborator

In Manage → Access, select Invite collaborator, enter the recipient email and choose Entire Assets space or a collection. Select only the necessary actions and create the invitation. Use Copy invitation to share the acceptance URL; the UI does not imply an email was sent.

Dashboard invitations expire after seven days. Only the invited account with that verified email can accept. Open the invitation while signed into that account and select Accept invitation. Invitation expiry limits acceptance; it does not automatically end an already accepted grant. Revoke access explicitly when the collaboration ends.

## Permission meanings

* read: view permitted assets. download\_original is separate; viewing a publication does not grant original-file access.
* upload: ingest into the space. process: request media processing. edit\_metadata: change descriptive metadata.
* review: approve or request changes to publication snapshots. publish: publish or withdraw approved media. These are distinct editorial responsibilities.
* curate: organize collections subject to current read permissions. manage\_policy: configure governance. manage\_access: create/change/revoke explicit sharing.
* upload, manage\_policy and manage\_access are space-only actions; the collection invitation form excludes them. The dashboard always includes read.
* An API key also needs the endpoint’s assets:\* scope. A key scope does not bypass the account’s space/collection permissions.

## Server-side SDK scope and access management

Never place the account API key in browser code. Set assetSpaceId when constructing the SDK; it sends X-Asset-Space-ID for Assets management calls. Resolve the allowed space from trusted application state instead of accepting an arbitrary browser header.

```ts theme={null}
import { StackShift } from '@stackshift-cloud/sdk'

const apiKey = process.env.STACKSHIFT_API_KEY
const assetSpaceId = process.env.STACKSHIFT_ASSET_SPACE_ID
if (!apiKey || !assetSpaceId) throw new Error('Missing Assets server configuration')
const sdk = new StackShift({ apiKey, assetSpaceId })
const { spaces } = await sdk.assets.dam.spaces()
const invitation = await sdk.assets.dam.invite({
  email: 'editor@example.com', actions: ['read', 'edit_metadata'],
  collection_id: collectionId,
  expires_at: new Date(Date.now() + 7 * 86400000).toISOString(),
})
// Later, use the current collaborator record and revision.
await sdk.assets.dam.updateCollaborator(invitation.id, invitation.revision, ['read'])
```

## Change or revoke access

Edit changes a grant’s actions. To change its email or scope, create the correct invitation and revoke the obsolete grant. Revoke requires confirmation in the dashboard and invalidates that grant; other independent grants can still authorize access.

The service rechecks current authority for metadata, publication, delivery and asynchronous processing. Do not rely on a cached library response after revocation. Previously downloaded files cannot be erased from a recipient’s device.

The collaborator routes are /api/v1/assets/collaborators and /collaborators/\{id}; listing, inviting, editing and revoking require assets:admin plus access-management authority. Accepting an invitation uses POST /api/v1/assets/invitations/\{id}/accept with the invited account and assets:read. API edits/revocation also require the grant’s current If-Match revision.

## Recover from errors

If access cannot load, check the selected space and access-management permission. If acceptance fails, verify the signed-in email, its verification state and invitation expiry. If a collection update conflicts, reload the collection and compare membership before using the new revision; do not retry an old revision indefinitely.

If a search unexpectedly returns no results, clear filters one at a time and check the space, collection and permission boundary. Do not broaden a production service credential to hide a missing grant.

## Go, Python and PHP: collaborators

Initialize the scoped dam client as shown in Assets SDKs. The expiry is the invitation acceptance deadline; choose the narrow actions the collaborator needs.

<CodeGroup>
  ```go Go theme={null}
  func inviteEditor(ctx context.Context, dam *stackshift.AssetDAMClient, email, expiresAt string) (map[string]any, error) {
  	return dam.Invite(ctx, map[string]any{
  		"email": email, "expires_at": expiresAt,
  		"actions": []string{"read", "edit_metadata"},
  	})
  }
  ```

  ```python Python theme={null}
  def invite_editor(dam, email, expires_at):
      return dam.invite({"email": email, "expires_at": expires_at,
                         "actions": ["read", "edit_metadata"]})
  ```

  ```php PHP theme={null}
  function inviteEditor(\StackShift\AssetDAMClient $dam, string $email, string $expiresAt): array {
      return $dam->invite(['email' => $email, 'expires_at' => $expiresAt,
          'actions' => ['read', 'edit_metadata']]);
  }
  ```
</CodeGroup>

## HTTP operations for this workflow

Management requests use Authorization: Bearer with your server-side Assets credential and X-Asset-Space-ID for the selected space. API-key scopes and space/collection permissions both apply. Successful JSON responses use \{success: true, data: ...}; the SDK methods return the unwrapped data. Paths shown outside /api/v1 use their dedicated short-lived credential.

| Operation                                               | Authorization | Concurrency     | Success |
| ------------------------------------------------------- | ------------- | --------------- | ------- |
| `GET /api/v1/assets/spaces`                             | assets:read   | —               | 200     |
| `GET /api/v1/assets/collaborators`                      | assets:admin  | —               | 200     |
| `POST /api/v1/assets/collaborators`                     | assets:admin  | —               | 201     |
| `PUT /api/v1/assets/collaborators/{collaboratorID}`     | assets:admin  | Quoted If-Match | 200     |
| `DELETE /api/v1/assets/collaborators/{collaboratorID}`  | assets:admin  | Quoted If-Match | 200     |
| `POST /api/v1/assets/invitations/{invitationID}/accept` | assets:read   | —               | 200     |

For If-Match, quote the revision number, for example If-Match: "7". On 412 assets.revision\_conflict, reload the relevant resource and reconcile the edit before retrying. Use the revision of the resource being changed: asset, collaborator, schema, publication or gallery. A bucket-policy save instead places its current revision in the JSON body.

## Collaborator request and response lifecycle

Invite with email, actions, optional collection\_id and optional expires\_at. The response is the collaborator/invitation record: id, asset\_space\_id, email, optional user\_id and collection\_id, actions, status, expires\_at, revision, invited\_by and timestamps. Persist its ID, not the invitee’s email, as the mutation target.

The invitee accepts with their own authenticated account through the invitation accept endpoint. Do not use the inviter’s credential to impersonate acceptance. The returned access and status apply to that account in the shared space.

Update sends \{actions: \[...]} with the current collaborator revision; revoke uses DELETE and that revision. Both return \{saved: true}, not a new collaborator object. Reload collaborators before a later mutation. Changing collection scope requires managing the intended grant explicitly; update changes actions only.

Spaces returns \{spaces: \[...]}; each space supplies id, owner\_id, name, actions and collection\_ids. Collaborators returns \{collaborators: \[...]}. Select a space before listing its library; the same UUID in a request does not grant access without the corresponding membership and collection permissions.

## Expected result

<Check>
  The library reflects actual server-side membership and permissions, and collaborators can perform only the actions explicitly granted to them.
</Check>

## Common failures

<Warning>
  * Treating project membership as an Assets grant.
  * Confusing saved-search criteria with collection membership.
  * Assuming invitation expiry revokes an accepted grant.
</Warning>

## Related guides

<CardGroup cols={2}>
  <Card title="Typed metadata and bucket governance" href="/assets/metadata-and-bucket-governance">
    Create and publish metadata schemas, bind exact revisions to buckets, validate asset fields, and migrate existing delivery into governance.
  </Card>

  <Card title="Publications, review and usage rights" href="/assets/publications-review-and-rights">
    Select immutable renditions, capture metadata and rights, submit editorial review, publish by channel, and withdraw delivery.
  </Card>

  <Card title="Embed the headless asset picker" href="/assets/headless-asset-picker">
    Add JavaScript or React media selection using expiring capabilities, save stable references, and resolve approved media from your backend.
  </Card>

  <Card title="Assets SDKs: JavaScript, Go, Python and PHP" href="/assets/sdk-media-workflows">
    Configure space-scoped clients and use native video, governed DAM, model rendering, galleries and CMS capabilities in all four SDKs.
  </Card>
</CardGroup>
