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

# Import generated videos from an external URL

> Copy an AI-generated MP4 into permanent Assets storage using a constrained remote-URL capability, then track readiness and deliver it safely.

## Goal

Store your own copy of an external video and retain its Assets ID independently of the provider URL.

## Prerequisites

* A direct HTTPS file URL that returns the video bytes, not a webpage
* A server-side Assets token with assets:write and an authorized space
* A destination bucket and enough storage allowance

## Workflow

<Steps>
  <Step>
    Wait for the external generator to finish and obtain its direct download URL.
  </Step>

  <Step>
    Create a fresh capability allowing remote URL ingestion.
  </Step>

  <Step>
    Fetch the source into Assets before the source URL expires.
  </Step>

  <Step>
    Persist the returned asset ID, wait for readiness and use Assets delivery URLs.
  </Step>
</Steps>

## Choose a usable source URL

The source must be reachable from StackShift and return HTTP 200 with an allowed Content-Type. The importer does not accept custom Authorization headers or browser cookies for the upstream server. If the provider requires those, download the bytes on your backend and upload the file normally, or request a provider-signed download URL.

Remote ingestion protects against private-network destinations. Do not use localhost, internal IPs or a URL that resolves to your private services. Use the final direct media URL rather than a share page or an HTML redirect/login flow. URL expiry must leave time for the complete fetch.

## Create a remote-ingestion capability

Run this on your backend. The example uses jq to read the success/data envelope. Set STACKSHIFT\_ASSETS\_TOKEN and STACKSHIFT\_ASSET\_SPACE\_ID in a protected environment. Replace the bucket, prefix and origin with your application values. Use a new unique object key for every generated result.

Unlike a general API key, this token is restricted to the specified origin and upload policy. Do not log the token or the provider download URL; either may grant temporary access.

```bash theme={null}
set -euo pipefail
API='https://api.stackshift.cloud'
CAPABILITY=$(curl --fail-with-body -sS -X POST "$API/api/v1/assets/upload-capabilities" \
  -H "Authorization: Bearer $STACKSHIFT_ASSETS_TOKEN" \
  -H "X-Asset-Space-ID: $STACKSHIFT_ASSET_SPACE_ID" \
  -H 'Content-Type: application/json' \
  --data '{
    "bucket": "generated-videos",
    "key_prefix": "generated/user_123",
    "allowed_mime_types": ["video/mp4"],
    "allowed_origins": ["https://app.example.com"],
    "max_bytes": 104857600,
    "allow_remote_url": true,
    "expires_in": "15m"
  }' | jq -er '.data.token')
```

## Copy the generated video

Set GENERATED\_VIDEO\_URL to the external provider download URL on your server. This request waits for ingestion and returns an asset record; it does not return a background import job. Keep the connection open while the file is transferred. The 100 MiB limit above is an example, not a platform-wide allowance.

The Origin must match the capability even when you call from a backend. Browser callers supply it automatically. Remote URL ingestion must use a fresh capability that has not been bound to a chunked session.

```bash theme={null}
jq -n --arg url "$GENERATED_VIDEO_URL" \
  --arg key "generated/user_123/result-unique-id.mp4" \
  '{url: $url, key: $key}' | \
curl --fail-with-body -sS -X POST "$API/assets/widget/remote-url" \
  -H "Authorization: Bearer $CAPABILITY" \
  -H 'Origin: https://app.example.com' \
  -H 'Content-Type: application/json' \
  --data-binary @- > imported-video.json
jq -er '.data.id' imported-video.json
```

## Permanent storage versus a permanent URL

Successful ingestion stores the file in the destination Assets bucket. The external generator URL may expire afterward without removing your Assets copy. Keep the returned asset ID and manage it through normal storage, retention and deletion rules; permanent storage does not mean an undeletable file.

Check the new asset status, scan and processing readiness. If it needs video editing or playback, prepare the current version through the native video API. Follow [trim and poster recipes](/assets/trim-videos-and-create-posters) for subsequent edits.

A private asset needs an authorized download URL; an expiring URL is not the permanent identifier. Public delivery requires an intentional public bucket/asset policy and readiness. Your application should not advertise the original provider URL as the stored result.

## Retries and rejected sources

Remote ingestion does not expose the chunked-session Idempotency-Key contract. If a connection fails after the transfer may have succeeded, first look up the exact destination bucket/key using your backend and reconcile the asset. Do not generate another key and blindly repeat the import.

A source served as application/octet-stream will fail a capability limited to video/mp4. Prefer a provider URL with the correct media Content-Type or download and upload through your backend; do not broaden all customer upload policies merely to make one provider response pass.

The current remote capability endpoint accepts url and key; it does not accept a custom maximum video duration, upstream headers or a visibility override. Bucket/content rules and subsequent video validation still apply.

## Expected result

<Check>
  Store your own copy of an external video and retain its Assets ID independently of the provider URL.
</Check>

## Common failures

<Warning>
  * An HTML share page is not a direct video download.
  * Expired source URLs, private-network destinations, non-200 responses or MIME mismatches reject ingestion.
  * A successful transfer still needs scan/processing readiness; it does not bypass video review or plan limits.
</Warning>

## Related guides

<CardGroup cols={2}>
  <Card title="Secure browser video uploads" href="/assets/secure-browser-video-uploads">
    Issue constrained upload credentials from your backend, upload video chunks from the browser, and distinguish enforced file limits from duration checks.
  </Card>

  <Card title="Trim videos, create JPG posters and download MP4s" href="/assets/trim-videos-and-create-posters">
    Copyable REST recipes for start/end trimming with audio, timestamp JPEG posters, job polling and signed download URLs for external AI services.
  </Card>

  <Card title="Private assets and signed URLs" href="/assets/private-assets-and-signed-urls">
    Keep files private, mint version-bound download tokens, and avoid accidentally caching or logging protected media.
  </Card>

  <Card title="Cloudinary, S3, R2, and S2 migrations" href="/assets/cloudinary-s3-and-s2-migrations">
    Inventory, dry-run, import, deduplicate, retry, cancel, and report durable migrations from Cloudinary or S3-compatible storage into StackShift Assets.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.