assets-pro / php-sdk
Typed AssetsPro API client with optional Laravel integration.
Requires
- php: ^8.3
- ext-curl: *
- ext-json: *
- guzzlehttp/guzzle: ^7.8.2 || ^8.0
- psr/http-message: ^2.0
Requires (Dev)
- laravel/pint: ^1.24
- orchestra/testbench: ^10.0 || ^11.0
- pestphp/pest: ^3.8 || ^4.0
- phpstan/phpstan: ^2.1
Suggests
- illuminate/support: Required for the optional Laravel 12/13 provider and facade.
Provides
None
Conflicts
- illuminate/support: <12.0 || >=14.0
Replaces
None
README
A typed PHP client for the AssetsPro API, with optional Laravel integration.
Use it to work with:
- Assets
- Uploads and downloads
- Brands, categories, tags, and collections
- Storage connections
- Publications and exports
- Members and invitations
- API tokens
- Share links
Requirements
- PHP 8.3+
ext-curlext-json- Guzzle 7 or 8
ext-fileinfofor automatic MIME detection- Laravel 12 or 13 if using the Laravel integration
You will also need:
- Your AssetsPro base URL
- An API token
- An organization ID for organization-scoped operations
Installation
composer require assets-pro/php-sdk
Basic Usage
Configure your credentials:
export ASSETSPRO_BASE_URL='https://assets.example.com' export ASSETSPRO_TOKEN='your-api-token' export ASSETSPRO_ORGANIZATION_ID='your-organization-id'
Create a client:
use AssetsPro\Client; $client = new Client( baseUrl: getenv('ASSETSPRO_BASE_URL'), token: getenv('ASSETSPRO_TOKEN'), organizationId: getenv('ASSETSPRO_ORGANIZATION_ID'), );
You can then access resources through the client:
$asset = $client->assets()->get($assetId); $assets = $client->assets()->list(); $brands = $client->brands()->list(); $collections = $client->collections()->list();
Most API responses are returned as typed data objects.
Organization Scope
Most operations require an organization.
You can create an unscoped client to discover organizations:
$client = new Client($baseUrl, $token); $organizations = $client->organizations()->list();
Then create an organization-scoped client:
$client = $client->forOrganization($organizationId);
forOrganization() returns a new client and leaves the original client unchanged.
Laravel
Laravel package discovery automatically registers the service provider and facade.
Add the following to your .env:
ASSETSPRO_BASE_URL=https://assets.example.com ASSETSPRO_TOKEN=your-api-token ASSETSPRO_ORGANIZATION_ID=your-organization-id
Publish the configuration if you need to customize it:
php artisan vendor:publish \
--provider='AssetsPro\Laravel\AssetsProServiceProvider' \
--tag=assetspro-config
Use dependency injection:
use AssetsPro\Client; final class AssetController { public function __construct( private readonly Client $assetsPro, ) { } }
Or use the facade:
use AssetsPro\Laravel\AssetsPro; $asset = AssetsPro::assets()->get($assetId);
Assets
List assets
use AssetsPro\Requests\AssetFilters; $page = $client->assets()->list( new AssetFilters( q: 'campaign', perPage: 25, ), ); foreach ($page as $asset) { echo $asset->name; }
Filters are available for search, taxonomy, uploader, dates, dimensions, media type, status, sorting, and pagination.
Get an asset
$asset = $client->assets()->get($assetId);
Update an asset
use AssetsPro\Requests\AssetUpdate; $asset = $client->assets()->update( $assetId, new AssetUpdate( name: 'Updated name', description: 'Updated description', ), );
Optional fields are omitted unless supplied.
Explicit null, false, and empty arrays are preserved when supported by the API.
Archive, trash, and restore
$client->assets()->archive($assetId); $client->assets()->trash($assetId); $asset = $client->assets()->restore($assetId);
Uploads
The easiest way to upload a file is:
use AssetsPro\Requests\UploadFileOptions; $upload = $client->upload( '/path/to/photo.jpg', new UploadFileOptions( name: 'Campaign photo', collectionIds: [$collectionId], ), );
The SDK automatically:
- Initializes the upload
- Transfers the file directly to storage
- Completes the upload
To wait until processing finishes:
$upload = $client->upload( '/path/to/photo.jpg', wait: true, );
Replace an existing asset
$upload = $client->upload( '/path/to/replacement.jpg', new UploadFileOptions( targetAssetId: $assetId, ), );
Publish after processing
$upload = $client->upload( '/path/to/photo.jpg', new UploadFileOptions( publishAfterProcessing: true, ), );
You can explicitly wait for processing or publication:
$ready = $client->uploads()->waitForProcessing($upload->id); $published = $client->uploads()->waitForPublication($upload->id);
Uploads can also be inspected, retried, or cancelled through $client->uploads().
Downloads
Get a signed download URL
$url = $client->getDownloadUrl($assetId);
Signed URLs are temporary and should be generated when needed.
Stream a file
$stream = $client->download($assetId); try { while (! $stream->eof()) { echo $stream->read(64 * 1024); } } finally { $stream->close(); }
Download to disk
$bytesWritten = $client->downloadTo( $assetId, '/tmp/asset.jpg', );
The destination file must not already exist.
Public URLs
Retrieve the currently available public URL:
$url = $client->getPublicUrl($assetId);
You can also retrieve both configured public URLs:
$urls = $client->getPublicUrls($assetId); $customUrl = $urls->customUrl; $defaultUrl = $urls->defaultUrl;
These methods report the asset's current publication state. They do not publish the asset automatically.
Pagination
Most list operations return Page<T>.
$page = $client->assets()->list( new AssetFilters(perPage: 25), );
Access the current page:
foreach ($page as $asset) { echo $asset->name; }
Fetch the next page:
$nextPage = $page->next();
Or iterate across multiple pages:
foreach ($page->iterate() as $asset) { echo $asset->name; }
Pagination helpers retain the original filters.
Taxonomy
AssetsPro supports:
- Brands
- Brand roles
- Categories
- Tags
- Collections
These resources use the same general pattern:
$categories = $client->categories()->list(); $category = $client->categories()->get($categoryId);
Creating and updating taxonomy records uses typed request objects.
use AssetsPro\Requests\TaxonomyCreate; $category = $client->categories()->create( new TaxonomyCreate( name: 'Photography', ), );
Collections
Collections can organize assets without changing their publication state.
Create or reuse a collection by name:
$resolved = $client->collections()->resolve('Campaign Assets'); $collection = $resolved->data;
Collections can also be archived, trashed, restored, and processed in bulk.
Operations involving large numbers of assets may continue asynchronously on the server.
Storage
AssetsPro supports:
- Cloudflare R2
- Amazon S3
- S3-compatible storage
Create a storage connection:
use AssetsPro\Requests\StorageCreate; use AssetsPro\Requests\StorageCredentials; $connection = $client->storageConnections()->create( new StorageCreate( name: 'Private storage', provider: 'r2', region: 'auto', privateBucket: 'assets-private', usePathStyleEndpoint: true, credentials: new StorageCredentials( $accessKey, $secretKey, ), endpoint: $endpoint, ), );
Verify and activate it:
$connection = $client->storageConnections()->verify($connection->id); $connection = $client->storageConnections()->activate($connection->id);
Storage credentials are only required for storage administration.
Normal asset uploads and downloads do not expose storage credentials to the caller.
Publications
Publish an asset:
$publication = $client->publications()->publish($assetId);
Revoke publication:
$client->publications()->revoke($assetId);
Publication may continue processing after the initial request.
Use the public URL helpers to retrieve the currently available delivery URL.
Exports
Create a ZIP export:
$export = $client->exports()->create([ $firstAssetId, $secondAssetId, ]);
Check its status:
$export = $client->exports()->get($export->id);
Download it when ready:
if ($export->status === 'ready') { $client->exports()->downloadTo( $export->id, '/tmp/assets.zip', ); }
Exports are generated asynchronously and expire after the server's configured retention period.
Members and Invitations
List organization members:
$members = $client->members()->list();
Update a role:
$member = $client->members()->update( $userId, 'Editor', );
Invite a user:
use AssetsPro\Requests\InvitationCreate; $invitation = $client->invitations()->create( new InvitationCreate( email: 'user@example.com', role: 'Viewer', ), );
Permission checks are enforced by the AssetsPro API.
API Tokens
Create an API token:
use AssetsPro\Requests\TokenCreate; $result = $client->tokens()->create( new TokenCreate( name: 'Read-only integration', abilities: [ 'assets.view', 'assets.download', ], ), );
The plaintext token is returned only when it is created:
$token = $result->token;
Store it securely.
Tokens can also be listed, restricted by IP address, and revoked.
Share Links
Create a share link for an asset:
use AssetsPro\Requests\ShareCreate; $share = $client->shareLinks()->create( new ShareCreate( name: 'Client review', assetId: $assetId, allowDownloads: false, ), );
Share links can target either an asset or a collection.
They can optionally support:
- Expiration
- Password protection
- Downloads
Existing share links can be listed, updated, or revoked.
Data Objects
API responses are represented by typed, read-only data objects.
Properties use camelCase:
$asset->createdAt; $asset->activeRevision; $asset->thumbnailUrl;
Convert an object back to the original API-style representation:
$data = $asset->toArray();
To distinguish between a missing field and an explicitly null field:
if ($asset->has('pending_revision')) { $pending = $asset->pendingRevision; }
Errors
API failures throw ApiException.
Upload-specific failures throw UploadException.
use AssetsPro\Exceptions\ApiException; use AssetsPro\Exceptions\UploadException; try { $upload = $client->upload($path); } catch (UploadException $e) { $uploadId = $e->uploadId; $phase = $e->phase; } catch (ApiException $e) { $status = $e->status; $code = $e->errorCode; $errors = $e->errors; $requestId = $e->requestId; }
Useful exception information can include:
- HTTP status
- Error code
- Validation errors
- Retry information
- Request ID
- Upload ID and failed upload phase
Sensitive credentials are redacted from exception messages and debug output.
Retries
The SDK automatically retries appropriate read operations and selected safe-to-repeat operations.
Mutating requests that may create or change resources are generally not retried automatically when the result could be ambiguous.
For uploads and similar operations, use the provided status and recovery methods rather than blindly repeating the original request.
Client Options
Customize behavior with Options:
use AssetsPro\Options; $client = new Client( baseUrl: $baseUrl, token: $token, organizationId: $organizationId, options: new Options( timeoutSeconds: 30, connectTimeoutSeconds: 5, maxRetries: 2, ), );
Available options include:
- Request timeout
- Connection timeout
- Retry count
- Maximum retry delay
- Response size limits
- Cancellation
The defaults are suitable for most applications.
Development
From the SDK package directory:
composer install
composer test
composer analyse
composer format
composer validate --strict
Run the complete verification suite with:
composer check
The package includes automated tests for resource operations, typed response handling, pagination, uploads, downloads, retries, error handling, and Laravel integration.
Project Structure
src/
├── Client.php
├── Resources/
├── Requests/
├── Data/
├── Exceptions/
└── Laravel/
tests/
examples/
config/
Most application code only needs to interact with:
ClientResourcesRequestsData
The remaining classes support the SDK internally.
License
MIT LICENSE
Copyright © 2026 AssetsPro contributors.