# getPrivateTransformUrl

```ts
function getPrivateTransformUrl(
   asset, 
   opts, 
   signingKey, 
signOpts): Promise<string | null>;
```

Defined in: packages/asset-client/dist/index.d.ts:1164

The signed URL for a TRANSFORM of a private asset — an arbitrary width, crop
or format, not just the sizes that happen to be materialised.

```ts
const url = await getPrivateTransformUrl(
  asset,
  { width: 1280, format: "webp" },
  tenantSigningKey,
  { expiresInSeconds: 300 },
);
// → https://8ok.uk/a/5/t/format=webp,width=1280/<sha>.webp?exp=…&sig=…
```

Why this exists at all: a private asset that can only be served at the sizes
someone already generated is barely a product. The signed tree mirrors the
public one, transforms included.

Returns `null` when `opts` serialize to an empty DSL — same contract as
[getTransformUrl](/api/nitida/sdk/functions/gettransformurl/), because "no transform requested" is not an error,
it just means you wanted [getPrivateAssetUrl](/api/nitida/sdk/functions/getprivateasseturl/).

⚠️ **Backend only**, like every signer here. And note the width is a plain
`number`: a signed URL is a trusted caller, so the edge ladder does not
apply — the same rule `getSignedTransformUrl` already follows.

## Parameters

| Parameter | Type |
| ------ | ------ |
| `asset` | [`Pick`](/api/nitida/sdk/react/-internal-/type-aliases/pick/)\<[`AssetDTO`](/api/nitida/sdk/type-aliases/assetdto/), `"sha"`\> |
| `opts` | [`PrivateTransformOptions`](/api/nitida/sdk/type-aliases/privatetransformoptions/) |
| `signingKey` | `string` |
| `signOpts` | [`SignAccessOptions`](/api/nitida/sdk/type-aliases/signaccessoptions/) |

## Returns

`Promise`\<`string` \| `null`\>