> ## 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.

# Assets SDKs: JavaScript, Go, Python and PHP

> Configure space-scoped clients and use native video, governed DAM, model rendering, galleries and CMS capabilities in all four SDKs.

## Goal

Build the same governed media workflows in your application’s backend language.

## Prerequisites

* A server-side StackShift API key and the scopes required by the workflow.
* The asset-space ID selected for the acting account. An empty space ID selects the owner’s space.

## Workflow

<Steps>
  <Step>
    Install the SDK for your application language.
  </Step>

  <Step>
    Create a backend client and select the asset space.
  </Step>

  <Step>
    Use the language examples in the feature guides for processing, publication and delivery.
  </Step>

  <Step>
    Keep revisions, idempotency keys and temporary playback credentials attached to the operation they identify.
  </Step>
</Steps>

## Install the SDK

```bash theme={null}
npm install @stackshift-cloud/sdk

go get github.com/stackshiftCloud/assets-go

python -m pip install stackshift

composer require stackshift/assets
```

## Initialize and select an asset space

These examples run on your backend. STACKSHIFT\_API\_KEY and STACKSHIFT\_ASSET\_SPACE\_ID are application environment variables read by the examples; the selected space is sent as X-Asset-Space-ID. Changing this header does not grant membership or bypass collection permissions. Keep account keys out of browser bundles.

JavaScript applies assetSpaceId to its Assets requests. Go DAM(spaceID)/Video(spaceID), Python dam.for\_space(space\_id)/video.for\_space(space\_id), and PHP dam($spaceId)/video($spaceId) return scoped media clients. They do not mutate other clients or the scope of existing upload/library calls. Use the scoped clients consistently within the DAM/video workflow.

<CodeGroup>
  ```ts JavaScript theme={null}
  import { StackShift } from '@stackshift-cloud/sdk'
  const sdk = new StackShift({
    apiKey: process.env.STACKSHIFT_API_KEY!,
    assetSpaceId: process.env.STACKSHIFT_ASSET_SPACE_ID,
  })
  const dam = sdk.assets.dam
  const video = sdk.assets.video
  const { spaces } = await dam.spaces()
  ```

  ```go Go theme={null}
  package main

  import (
  	"context"
  	"fmt"
  	stackshift "github.com/stackshiftCloud/assets-go"
  	"log"
  	"net/http"
  	"os"
  	"time"
  )

  func main() {
  	sdk, err := stackshift.New(stackshift.Options{
  		APIKey:     os.Getenv("STACKSHIFT_API_KEY"),
  		HTTPClient: &http.Client{Timeout: 30 * time.Second},
  	})
  	if err != nil {
  		log.Fatal(err)
  	}
  	dam := sdk.Assets.DAM(os.Getenv("STACKSHIFT_ASSET_SPACE_ID"))
  	spaces, err := dam.Spaces(context.Background())
  	if err != nil {
  		log.Fatal(err)
  	}
  	fmt.Println(spaces)
  }
  ```

  ```python Python theme={null}
  import os
  from stackshift import StackShift

  sdk = StackShift(api_key=os.environ["STACKSHIFT_API_KEY"])
  space_id = os.environ.get("STACKSHIFT_ASSET_SPACE_ID", "")
  dam = sdk.assets.dam.for_space(space_id)
  video = sdk.assets.video.for_space(space_id)
  spaces = dam.spaces()["spaces"]
  ```

  ```php PHP theme={null}
  <?php
  require 'vendor/autoload.php';

  use StackShift\AssetsClient;

  $assets = new AssetsClient(getenv('STACKSHIFT_API_KEY'));
  $spaceId = getenv('STACKSHIFT_ASSET_SPACE_ID') ?: '';
  $dam = $assets->dam($spaceId);
  $video = $assets->video($spaceId);
  $spaces = $dam->spaces()['spaces'];
  ```
</CodeGroup>

## Method reference

The table lists equivalent operations. Go calls take context.Context first. Payload field names match the JSON API in every language. All clients unwrap the success/data envelope. Go uses typed schema, publication, gallery and rendering responses; other object responses are map\[string]any, and captions return \[]map\[string]any. Python returns dictionaries/lists, PHP associative arrays, and JavaScript uses the exported SDK types.

For paginated lists, JavaScript publications accepts an options object and galleries accepts a cursor string. Go Publications/Galleries accept url.Values, Python publications/galleries accept a query dictionary, and PHP publications/galleries accept an associative query array. Preview helpers use the authorized preview route; they do not publish content.

| JavaScript                         | Go                                 | Python                               | PHP                                 |
| ---------------------------------- | ---------------------------------- | ------------------------------------ | ----------------------------------- |
| `dam.spaces()`                     | `DAM.Spaces(...)`                  | `dam.spaces(...)`                    | `dam->spaces(...)`                  |
| `dam.collaborators()`              | `DAM.Collaborators(...)`           | `dam.collaborators(...)`             | `dam->collaborators(...)`           |
| `dam.invite(...)`                  | `DAM.Invite(...)`                  | `dam.invite(...)`                    | `dam->invite(...)`                  |
| `dam.acceptInvitation(...)`        | `DAM.AcceptInvitation(...)`        | `dam.accept_invitation(...)`         | `dam->acceptInvitation(...)`        |
| `dam.updateCollaborator(...)`      | `DAM.UpdateCollaborator(...)`      | `dam.update_collaborator(...)`       | `dam->updateCollaborator(...)`      |
| `dam.revokeCollaborator(...)`      | `DAM.RevokeCollaborator(...)`      | `dam.revoke_collaborator(...)`       | `dam->revokeCollaborator(...)`      |
| `dam.metadataSchemas()`            | `DAM.MetadataSchemas(...)`         | `dam.metadata_schemas(...)`          | `dam->metadataSchemas(...)`         |
| `dam.metadataEditor(...)`          | `DAM.MetadataEditor(...)`          | `dam.metadata_editor(...)`           | `dam->metadataEditor(...)`          |
| `dam.createMetadataSchema(...)`    | `DAM.CreateMetadataSchema(...)`    | `dam.create_metadata_schema(...)`    | `dam->createMetadataSchema(...)`    |
| `dam.updateMetadataSchema(...)`    | `DAM.UpdateMetadataSchema(...)`    | `dam.update_metadata_schema(...)`    | `dam->updateMetadataSchema(...)`    |
| `dam.publishMetadataSchema(...)`   | `DAM.PublishMetadataSchema(...)`   | `dam.publish_metadata_schema(...)`   | `dam->publishMetadataSchema(...)`   |
| `dam.bucketGovernance(...)`        | `DAM.BucketGovernance(...)`        | `dam.bucket_governance(...)`         | `dam->bucketGovernance(...)`        |
| `dam.saveBucketGovernance(...)`    | `DAM.SaveBucketGovernance(...)`    | `dam.save_bucket_governance(...)`    | `dam->saveBucketGovernance(...)`    |
| `dam.migrateBucketGovernance(...)` | `DAM.MigrateBucketGovernance(...)` | `dam.migrate_bucket_governance(...)` | `dam->migrateBucketGovernance(...)` |
| `dam.publications(...)`            | `DAM.Publications(...)`            | `dam.publications(...)`              | `dam->publications(...)`            |
| `dam.publicationRenditions(...)`   | `DAM.PublicationRenditions(...)`   | `dam.publication_renditions(...)`    | `dam->publicationRenditions(...)`   |
| `dam.createPublication(...)`       | `DAM.CreatePublication(...)`       | `dam.create_publication(...)`        | `dam->createPublication(...)`       |
| `dam.updatePublication(...)`       | `DAM.UpdatePublication(...)`       | `dam.update_publication(...)`        | `dam->updatePublication(...)`       |
| `dam.transitionPublication(...)`   | `DAM.TransitionPublication(...)`   | `dam.transition_publication(...)`    | `dam->transitionPublication(...)`   |
| `dam.resolvePublication(...)`      | `DAM.ResolvePublication(...)`      | `dam.resolve_publication(...)`       | `dam->resolvePublication(...)`      |
| `dam.resolvePublication(id, true)` | `DAM.PreviewPublication(...)`      | `dam.preview_publication(...)`       | `dam->previewPublication(...)`      |
| `dam.resolvePublicationAR(...)`    | `DAM.ResolvePublicationAR(...)`    | `dam.resolve_publication_ar(...)`    | `dam->resolvePublicationAR(...)`    |
| `dam.createPickerCapability(...)`  | `DAM.CreatePickerCapability(...)`  | `dam.create_picker_capability(...)`  | `dam->createPickerCapability(...)`  |
| `dam.revokePickerCapability(...)`  | `DAM.RevokePickerCapability(...)`  | `dam.revoke_picker_capability(...)`  | `dam->revokePickerCapability(...)`  |
| `dam.models(...)`                  | `DAM.Models(...)`                  | `dam.models(...)`                    | `dam->models(...)`                  |
| `dam.estimateRender(...)`          | `DAM.EstimateRender(...)`          | `dam.estimate_render(...)`           | `dam->estimateRender(...)`          |
| `dam.renderProduct(...)`           | `DAM.RenderProduct(...)`           | `dam.render_product(...)`            | `dam->renderProduct(...)`           |
| `dam.processModel(...)`            | `DAM.ProcessModel(...)`            | `dam.process_model(...)`             | `dam->processModel(...)`            |
| `dam.galleries(cursor)`            | `DAM.Galleries(...)`               | `dam.galleries(...)`                 | `dam->galleries(...)`               |
| `dam.createGallery(...)`           | `DAM.CreateGallery(...)`           | `dam.create_gallery(...)`            | `dam->createGallery(...)`           |
| `dam.updateGallery(...)`           | `DAM.UpdateGallery(...)`           | `dam.update_gallery(...)`            | `dam->updateGallery(...)`           |
| `dam.transitionGallery(...)`       | `DAM.TransitionGallery(...)`       | `dam.transition_gallery(...)`        | `dam->transitionGallery(...)`       |
| `dam.resolveGallery(...)`          | `DAM.ResolveGallery(...)`          | `dam.resolve_gallery(...)`           | `dam->resolveGallery(...)`          |
| `dam.resolveGallery(id, true)`     | `DAM.PreviewGallery(...)`          | `dam.preview_gallery(...)`           | `dam->previewGallery(...)`          |
| `dam.resolveGalleryAR(...)`        | `DAM.ResolveGalleryAR(...)`        | `dam.resolve_gallery_ar(...)`        | `dam->resolveGalleryAR(...)`        |
| `video.workspace(...)`             | `Video.Workspace(...)`             | `video.workspace(...)`               | `video->workspace(...)`             |
| `video.process(...)`               | `Video.Process(...)`               | `video.process(...)`                 | `video->process(...)`               |
| `video.createSession(...)`         | `Video.CreateSession(...)`         | `video.create_session(...)`          | `video->createSession(...)`         |
| `video.revokeSession(...)`         | `Video.RevokeSession(...)`         | `video.revoke_session(...)`          | `video->revokeSession(...)`         |
| `video.captions(...)`              | `Video.Captions(...)`              | `video.captions(...)`                | `video->captions(...)`              |
| `video.addCaption(...)`            | `Video.AddCaption(...)`            | `video.add_caption(...)`             | `video->addCaption(...)`            |
| `video.removeCaption(...)`         | `Video.RemoveCaption(...)`         | `video.remove_caption(...)`          | `video->removeCaption(...)`         |
| `video.review(...)`                | `Video.Review(...)`                | `video.review(...)`                  | `video->review(...)`                |
| `video.analytics(...)`             | `Video.Analytics(...)`             | `video.analytics(...)`               | `video->analytics(...)`             |
| `video.renewSession(...)`          | `Video.RenewSession(...)`          | `video.renew_session(...)`           | `video->renewSession(...)`          |
| `video.events(...)`                | `Video.Events(...)`                | `video.events(...)`                  | `video->events(...)`                |

## Revisions and idempotency

Update and transition methods accept the last returned revision and send a quoted If-Match header. Read the returned object after each edit or transition and use its new revision. If a request conflicts with another edit, fetch and reconcile the latest state before retrying. Do not automatically replay approval against a different snapshot.

processModel/ProcessModel/process\_model and renderProduct/RenderProduct/render\_product accept an idempotency key. Reuse the key for retries of the same operation. For rendering, estimate the exact version/checksum/recipe/outputs, obtain acceptance of those units, and pass that maximum to submission. A new user decision or changed input is a different operation.

Python and PHP accept JSON-shaped inputs. Go inputs use map\[string]any; retain JSON booleans/numbers/lists rather than turning them into strings. An empty list and false/0 can carry an intentional policy or rendering value and are transmitted unchanged.

## Video sessions and CMS responses

Create playback sessions with the account-authorized video client. RenewSession/renew\_session/renewSession and Events/events use only the playback credential and the /playback path on the configured API origin; they do not send the account key or asset-space header. Keep renewal tied to the authorized viewer and asset and propagate access denials instead of retrying with an account key.

Authorize the viewer and the application’s saved publication/gallery ID before calling a resolver. Return the unwrapped delivery object from your backend with Cache-Control: private, no-store. Picker callbacks return \{token}; video callbacks return the playback grant; gallery/publication callbacks return the corresponding delivery object. See each integration guide for its exact callback contract.

## Handle errors without losing the current media

Management failures propagate to the caller. In JavaScript inspect StackShiftAPIError; in Go use errors.As with \*stackshift.APIError; in Python catch StackShiftAPIError from stackshift.client; in PHP catch RuntimeException. Preserve a previous valid publication while replacement processing runs.

Do not retry authentication failures or revision conflicts blindly. Reconcile the current authorization or revision first. Poll the returned job/package/render-set state after acceptance: a successful submission response identifies queued work, not completed media. Configure the application’s HTTP timeout and cancellation policy; Go accepts a custom HTTPClient and every operation takes a context.

## HTTP errors and safe retries

Handle the SDK exception/error before reading a response object. Preserve the HTTP status and machine-readable error code when your application transport exposes them. Return safe user-facing text from your backend and keep credentials and signed URLs out of diagnostics.

| Field                                      | Meaning and constraints                                                                                                                                                        |
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `400`                                      | Invalid JSON, field type, metadata value, policy or unsupported media selection. Correct the request before retrying.                                                          |
| `401`                                      | Missing/expired authentication on session endpoints; obtain a new authorized session when renewal cannot continue.                                                             |
| `402`                                      | The current Free or paid storage/processing allowance is exhausted. Keep the editor’s work and upgrade or wait for the monthly processing reset instead of retrying in a loop. |
| `403`                                      | Wrong space, missing permission, revoked/private delivery or invalid capability. Never substitute public delivery to bypass denial.                                            |
| `404`                                      | Resource is absent or not visible on the requested route; refresh the saved reference and access context.                                                                      |
| `409 assets.idempotency_conflict`          | A request key was reused for a different operation. Recover the original operation; use a new key only for a deliberately new request.                                         |
| `409 assets.governance_migration_required` | A populated bucket needs explicit migration before governed delivery can be enabled.                                                                                           |
| `412 assets.revision_conflict`             | The resource changed since it was read. Refetch, reconcile and repeat the intended mutation using the fresh revision.                                                          |
| `422 assets.checksum_mismatch`             | Submitted source/output checksum does not match the bytes. Refresh the version binding; do not suppress validation.                                                            |
| `429`                                      | Rate limit reached. Honor Retry-After when present and reduce event/request frequency.                                                                                         |
| `503`                                      | Required storage, processing or provider service is unavailable. Retain stable job/request IDs and retry reads with bounded backoff; do not create duplicate paid work.        |

## Expected result

<Check>
  Your backend uses the selected language SDK for the same authorized media lifecycle, and browser embeds receive only bounded capabilities or delivery grants.
</Check>

## Common failures

<Warning>
  * Using a revision from before the last successful update.
  * Selecting the wrong space for a shared library.
  * Putting account keys or temporary delivery URLs into permanent browser configuration.
</Warning>

## Related guides

<CardGroup cols={2}>
  <Card title="Library, spaces, collections and sharing" href="/assets/library-spaces-and-sharing">
    Find and organize media, switch Assets spaces, invite collaborators with explicit permissions, and handle access changes safely.
  </Card>

  <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="Native video processing and secure playback" href="/assets/video-scanning-and-governance">
    Version-pinned video packages, scan and review gates, replacement behavior, playback sessions, API routes, and failure recovery.
  </Card>

  <Card title="Upload, validate and publish 3D models" href="/assets/models-ar-and-galleries">
    Prepare supported GLB and supplied USDZ files, inspect validation reports, add posters, review the model and launch authorized device AR.
  </Card>

  <Card title="Product rendering: images, spins and turntables" href="/assets/cloudinary-product-rendering">
    Use model-preview controls, estimate rendering units, request proofs or final media, and publish approved immutable outputs.
  </Card>

  <Card title="Create and embed media galleries" href="/assets/media-galleries">
    Arrange published images, video, spins and models with locale, alternate text, approved fallbacks and stable published revisions.
  </Card>
</CardGroup>
