Model APIs
File access controls
On this page
Library access follows the caller's personal or organization scope. To give someone access without an account, create a share link with a deadline, keep the returned URL, and revoke the link when it is no longer needed.
There is no per-file ACL on the storage object itself; access is the Library scope plus share links.
How access works #
| Request and caller | Result | Why |
|---|---|---|
GET /assets/{id} for your live asset in personal scope |
200 with an asset and signed URL |
The asset is in your Library |
GET /assets/{id} for a teammate's live asset in your active organization |
200 with an asset and signed URL |
Organization assets belong to the shared Library; user_id still identifies the creator |
| Share management for an asset outside the caller's Library scope | 404 |
The target must be visible in the current scope |
| Create a share link as a viewer or billing role | 403 Read-only role |
Reading the Library does not grant sharing authority |
| Create a share link as a member for another teammate's asset | 403 Forbidden |
Members may share only assets they created; owners/admins may share any |
| Public resolver for an active share link | 302 to temporary media |
No account or bearer token is required |
| Public resolver for a revoked/expired link or trashed/deleted media | 410 |
The link no longer provides access |
| Public resolver for an unknown token | 404 |
No matching link exists |
| Public resolver over its per-IP limit | 429, Retry-After: 60 |
Wait before trying again |
The 302 is the resolver response, not the media body. Follow its Location to fetch the bytes.
Library scope #
Personal assets belong to your personal Library. In organization context, teammates can read the shared Library, while the asset's user_id continues to name its creator. Choose the active context with PUT /me/active-organization; organization API keys stay bound to their organization. See Teams and organizations for switching context and roles.
| Role or scope | Create a share link | Revoke a share link |
|---|---|---|
| Personal asset owner | Own ready, untrashed assets | Links on own assets |
| Organization owner/admin | Any ready, untrashed asset in that organization | Links on any asset in that organization |
| Organization member | Assets they created | Links they created or links on assets they can change |
| Organization viewer/billing | Refused with 403 Read-only role |
Refused with 403 Read-only role |
Share links #
Create a link #
| Property | Value |
|---|---|
| Endpoint | POST /assets/{id}/share |
| Default lifetime | 30 days |
| Custom lifetime | expires_in_days, an integer from 1 through 365 |
| Success | 201 with a ShareLink |
| Returned once | token and url; later listings cannot recover either |
| Field | Type | Required | Description |
|---|---|---|---|
expires_in_days |
integer | No | Lifetime of the link in days from now. Defaults to 30. Ignored when never_expires is true. |
never_expires |
boolean | No | A link with no end date. Refused (400, code share_expiry_exceeds_organization_limit) when the organization sets a maximum lifetime. |
password |
string | No | Viewers must enter this password before the media is served. Stored only as an argon2id hash; never returned. |
view_only |
boolean | No | Turn downloads off for this link. The share page offers no Download and the resolver refuses download=true. A viewer who can play media can still capture it, so treat this as a deterrent, not copy protection. |
Set ASSET_ID to a ready asset you are allowed to share. These request examples use a seven-day lifetime. The CLI currently has no share command; its tab uses curl with the same NOLGIA_TOKEN credential.
curl --fail-with-body -sS "https://api.nolgia.ai/v1/assets/$ASSET_ID/share" \
-H "Authorization: Bearer $NOLGIA_TOKEN" \
-H "Content-Type: application/json" -d '{"expires_in_days":7}'
curl --fail-with-body -sS "https://api.nolgia.ai/v1/assets/$ASSET_ID/share" \
-H "Authorization: Bearer $NOLGIA_TOKEN" \
-H "Content-Type: application/json" -d '{"expires_in_days":7}'
import { createNolgiaClient } from "@nolgia/sdk";
const nolgia = createNolgiaClient(process.env.NOLGIA_TOKEN!);
const { data: link, error } = await nolgia.POST("/assets/{id}/share", {
params: { path: { id: process.env.ASSET_ID! } },
body: { expires_in_days: 7 },
});
if (error) throw new Error(`${error.title}: ${error.detail ?? ""}`);
console.log(link.id, link.url);
import os
from uuid import UUID
from nolgia import AuthenticatedClient
from nolgia.api.sharing import create_asset_share_link
from nolgia.models import CreateShareLinkRequest, ShareLink
client = AuthenticatedClient(base_url="https://api.nolgia.ai/v1", token=os.environ["NOLGIA_TOKEN"])
link = create_asset_share_link.sync(
UUID(os.environ["ASSET_ID"]), client=client,
body=CreateShareLinkRequest(expires_in_days=7),
)
if not isinstance(link, ShareLink):
raise SystemExit(f"refused: {link}")
print(link.id, link.url)
use nolgia_client::ClientBuilder;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = ClientBuilder::new("https://api.nolgia.ai/v1")
.bearer_token(std::env::var("NOLGIA_TOKEN")?)
.build()?;
let id = std::env::var("ASSET_ID")?.parse::<uuid::Uuid>()?;
let link = client.create_asset_share_link().id(id)
.body_map(|b| b.expires_in_days(7_u64))
.send().await?.into_inner();
println!("{} {:?}", link.id, link.url);
Ok(())
}
This is the production create response from share-create.json; the token and the URL's token are redacted. The SDK snippets are examples, not separate live captures.
{
"access_count": 0,
"created_at": "2026-09-21T03:33:52.874514Z",
"created_by": "dad27b53-a85b-4e3d-8fd6-b152c803a27c",
"expires_at": "2026-09-28T03:33:52.873377Z",
"id": "26d786f5-437c-4da4-9f04-60342191299b",
"kind": "asset",
"target_id": "f7bc037c-d7d7-434a-8843-26675574de0d",
"token": "…",
"token_prefix": "EJIkUdv1",
"url": "https://nolgia.ai/s/…"
}
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | Yes | |
kind |
ShareLinkKind |
Yes | |
target_id |
string | Yes | The shared asset's id, or the shared render's id. |
token |
string | No | The share token (base64url, 128 bits of entropy). Create response only. |
token_prefix |
string | Yes | The first characters of the token, for recognising a link in a listing. |
url |
string | No | The public share URL to hand out (the nolgia.ai share page). Create response only. |
expires_at |
string | No | When the link stops resolving. Absent for a link that never expires. |
has_password |
boolean | No | Viewers must enter a password. |
view_only |
boolean | No | Downloads are turned off for this link. |
created_at |
string | Yes | |
created_by |
string | Yes | The user who created the link. |
access_count |
integer | Yes | Successful public resolutions so far. |
last_accessed_at |
string | No | When the link was last resolved. Absent until the first visit. |
revoked_at |
string | No | Set once the link has been revoked. Listings only return unrevoked links. |
List active links #
| Property | Value |
|---|---|
| Endpoint | GET /assets/{id}/share |
| Order | Newest first |
| Included | Unrevoked, unexpired links |
| Omitted | Tokens and URLs |
| Management handle | Use each link's id to revoke it |
curl --fail-with-body -sS "https://api.nolgia.ai/v1/assets/$ASSET_ID/share" \
-H "Authorization: Bearer $NOLGIA_TOKEN"
{
"links": [
{
"access_count": 1,
"created_at": "2026-09-21T03:33:52.874514Z",
"created_by": "dad27b53-a85b-4e3d-8fd6-b152c803a27c",
"expires_at": "2026-09-28T03:33:52.873377Z",
"id": "26d786f5-437c-4da4-9f04-60342191299b",
"kind": "asset",
"last_accessed_at": "2026-09-21T03:33:53.01188Z",
"target_id": "f7bc037c-d7d7-434a-8843-26675574de0d",
"token_prefix": "EJIkUdv1"
}
]
}
| Response field | Meaning |
|---|---|
links |
Active links, each using the ShareLink fields above without token or url |
links[].id, links[].token_prefix |
Revoke handle and short recognition aid |
links[].kind, links[].target_id |
Whether the target is an asset/render and which target it is |
links[].created_by, links[].created_at, links[].expires_at |
Creator, creation time and share deadline |
links[].access_count, links[].last_accessed_at |
Successful public resolutions and the latest one; metadata reads do not increment the count |
Resolve the media #
| Property | Value |
|---|---|
| Endpoint | GET /share/{token}; no authentication |
| Success | 302 with a fresh signed media URL in Location |
| Media URL lifetime | About 15 minutes |
| Caching | Cache-Control: no-store on the redirect |
| Download | Add ?download=true for a Content-Disposition: attachment media URL |
Set SHARE_TOKEN to the token returned at creation. This command shows the resolver response without following it; add -L to fetch the media.
curl -sS -D - -o /dev/null "https://api.nolgia.ai/v1/share/$SHARE_TOKEN"
These are the real response headers from share-resolve.headers; location is truncated.
HTTP/2 302
access-control-allow-headers: Authorization, Content-Type, X-Request-Id, If-Match, Idempotency-Key
access-control-allow-methods: GET, POST, PUT, PATCH, DELETE, OPTIONS
access-control-allow-origin: *
cache-control: no-store
content-type: text/html; charset=utf-8
location: https://storage.googleapis.com/nolgia-generations-prod/dad27b53-a85b-4e…
x-cloud-trace-context: 978e41fdddedef4cd8db4b39631ad0cd
date: Mon, 21 Sep 2026 03:33:53 GMT
server: Google Frontend
content-length: 941
via: 1.1 google
alt-svc: h3=":443"; ma=2592000
| Response field | Meaning |
|---|---|
HTTP status 302 |
Follow the redirect; this response is not the media |
location |
Short-lived download URL, refreshed on each resolution |
cache-control |
no-store; do not cache the resolver response |
content-type, content-length |
The redirect's HTML body, not the media's MIME type or size |
date, trace and infrastructure headers |
Response metadata, not share-link settings |
access-control-* |
Cross-origin response headers |
A valid share URL can outlast many media URLs. Keep the share URL and resolve it when needed; do not persist the Location as a replacement for the share link.
Revoke a link #
| Property | Value |
|---|---|
| Endpoint | DELETE /assets/{id}/share/{token} |
{token} accepts |
The original token or the link id from the listing |
| Success | 204, no response body; already revoked is also 204 |
| Wrong target | 404 if the link does not exist or belongs to another asset |
| Next public resolution | 410 Gone |
The path parameter is named token, but the captured revoke used the link id 26d786f5-437c-4da4-9f04-60342191299b and returned 204. Use your own listing's id as SHARE_LINK_ID.
curl --fail-with-body -sS -X DELETE \
"https://api.nolgia.ai/v1/assets/$ASSET_ID/share/$SHARE_LINK_ID" \
-H "Authorization: Bearer $NOLGIA_TOKEN"
HTTP/2 204
There is no JSON body. The same public resolver request now returns the real problem captured in share-gone.json:
curl -sS "https://api.nolgia.ai/v1/share/$SHARE_TOKEN"
{
"detail": "this share link has been revoked",
"request_id": "localhost/7bnUUXx7b2-007297",
"status": 410,
"title": "Gone",
"type": "about:blank"
}
| Response field | Meaning |
|---|---|
status, title |
410 Gone: the link no longer resolves |
detail |
This capture identifies revocation; expired links and deleted media give their own reason |
request_id |
Correlation id to include when asking for help |
type |
Problem type; about:blank here |
Revocation stops future resolutions. A previously issued signed media URL has its own short expiry; revoking the share does not change that URL's signature.
Preview metadata and renders #
GET /share/{token}/meta returns the title, modality, media URL and expiry for a preview page or embed, with an optional description, thumbnail, duration and size. It has the same unknown/revoked/rate-limit behavior as the resolver, but does not increment the link's access count.
| Field | Type | Required | Description |
|---|---|---|---|
kind |
ShareLinkKind |
Yes | |
title |
string | Yes | The asset's display name (or a trimmed prompt), or the composition's name for a render. |
description |
string | No | A short description when one is known (the generation prompt, or the composition's description). |
model |
string | No | Catalog id of the model that generated the shared media (render it with the catalog's display name, never raw).… |
preset |
ShareLinkPreset |
No | |
modality |
Modality |
Yes | |
mime_type |
string | No | |
media_url |
string | Yes | Freshly signed short-lived URL of the media itself. Play or embed it now; never store it. |
media_expires_at |
string | Yes | When media_url (and thumbnail_url) stop working. Fetch the metadata again for a new one. |
thumbnail_url |
string | No | Freshly signed short-lived poster image, when the media has one. |
duration_seconds |
number | No | Media duration for video and audio, when known. |
size_bytes |
integer | No | |
expires_at |
string | No | When the share link itself stops resolving. Absent for a link that never expires. |
view_only |
boolean | No | Downloads are turned off for this link; offer no Download. |
created_at |
string | Yes | |
creator_insider |
InsiderBadge |
No | Present only when the link's owner is an active NOLGIA Insider (who chose a public profile) and the link is a personal one: their badge, linking to nolgia.ai/@handle. Absent for everyone else. |
Renders use the same create, list and revoke pattern under /renders/{id}/share and /renders/{id}/share/{token}. Their visibility follows the composition's Library scope. Both kinds resolve through the same public /share/{token} and /share/{token}/meta routes.
| Method | Path | What it does |
|---|---|---|
| GET | /assets/{id}/share |
List the active share links on an asset. |
| POST | /assets/{id}/share |
Create a durable public share link for an asset. |
| DELETE | /assets/{id}/share/{token} |
Revoke a share link on an asset. |
| GET | /renders/{id}/share |
List the active share links on a render. |
| POST | /renders/{id}/share |
Create a durable public share link for a finished render. |
| DELETE | /renders/{id}/share/{token} |
Revoke a share link on a render. |
| GET | /me/share-links |
List every active share link in the caller's library, newest first. |
| GET | /me/share-links/policy |
The share-link rules that apply in the caller's current space. |
| GET | /share/{token} |
Public resolver (no authentication) that redirects to the shared media. |
| GET | /share/{token}/meta |
Public metadata (no authentication) for a share link's preview page. |
| POST | /share/{token}/unlock |
Trade a share link's password for a short-lived viewing grant (no authentication). |

