Library / SDK
laravel/forge-sdk avatar
laravel/forge-sdk

laravel/forge-sdk: a PHP client for Forge API v2, built around organization slugs

The official Laravel Forge PHP SDK.

583 stars102 forksPHPMIT

At a glance

What is it?
The official Laravel Forge SDK is a PHP package that wraps Forge's API v2, and the defining change in version 4 is that every endpoint now takes an organization slug as its first parameter. It also adds a cursor paginator with lazy iteration and an option to wait for long-running server actions to finish.
Who is it for?
laravel-forge-sdk suits a team provisioning Forge servers from PHP, because it covers the full resource surface from servers and sites through domains, certificates and deployments, and the cursor paginator means listing a large fleet does not require reading every page into memory. It does not suit you if you are on API v1, because version 4 is a breaking change and every call needs an organization slug.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 7 days ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Version 4 is a breaking change, and the slug is the reason

The Laravel Forge SDK provides an expressive interface for interacting with Forge's API v2 and managing Laravel Forge servers, and it is the official package for it.

The single most important thing to know before you write any code against it is the version 4 change. Version 4.0 uses the Forge API v2 and introduces significant breaking changes, and the reason is stated in one line: all endpoints now require an organization slug as the first parameter.

That is a wide-reaching change, because it applies to every method in the SDK. Fetching a server becomes servers($organizationSlug) rather than servers(), creating a site becomes createSite($organizationSlug, $serverId, $data), and so on. The upgrade guide in the repository, UPGRADE-4.0.md, is what the documentation points you to for detailed migration instructions, and there is a separate UPGRADE.md for earlier transitions.

The slug itself comes from the SDK. The organizations() call returns the organizations you have access to, and each one carries a slug property, so the typical pattern is to look it up and use it from then on:

php
// Get all organizations you have access to
$organizations = $forge->organizations();

// Use the organization slug
$organizationSlug = $organizations[0]->slug;

The default branch is named 4.x, which is a signal about how the project is versioned: the major version tracks the Forge API major version rather than following semantic versioning independently of the service. A 4.0.2, a 4.1.0 and a 4.1.1 exist in the release list, and the last push was on 2026-10-16, with 4.1.1 published on 2026-10-07.

Instantiating the client and creating a server

The client is constructed with an API token and nothing else:

php
$forge = new Laravel\Forge\Forge($apiToken);

That is the whole setup, and everything else in the SDK hangs off that instance. The documentation also covers the authenticated user, with both a user() and a me() method:

php
$user = $forge->user();
// or
$user = $forge->me();

The richest example in the README is server creation, because it shows how the enums are used for the values that would otherwise be magic strings:

php
// Create a new server
$server = $forge->createServer($organizationSlug, [
    "provider" => ServerProviders::DIGITAL_OCEAN,
    "credential_id" => 1,
    "name" => "my-server",
    "type" => "app",
    "size" => "01",
    "database_type" => InstallableServices::POSTGRES,
    "php_version" => InstallableServices::PHP_85,
    "region" => "ams2"
]);

So the provider is an enum constant, ServerProviders::DIGITAL_OCEAN, and the database and PHP versions come from InstallableServices, with InstallableServices::POSTGRES and InstallableServices::PHP_85. The name, type, size and region are plain strings, which tells you the SDK has enumerated the values where the set is closed and where it is open.

The remaining management calls follow the same shape. There are servers($organizationSlug) to list, server($organizationSlug, $serverId) to fetch one, deleteServer to remove it, createServerAction to trigger an action such as a reboot, and archivedServers to list the ones that have been archived rather than deleted.

A cursor paginator that can be walked lazily

The collection methods, servers(), sites() and recipes(), return a Laravel\Forge\CursorPaginator rather than a plain array, and the SDK offers three ways to consume it.

php
// Iterate the current page (foreach hides pagination details)
foreach ($forge->servers($organizationSlug) as $server) {
    echo $server->name;
}

// Lazily iterate across all pages — fetches the next cursor on demand
foreach ($forge->servers($organizationSlug)->lazy() as $server) {
    echo $server->name;
}

// Snapshot the current page as a plain array (also: json_encode($paginator))
$page = $forge->servers($organizationSlug)->toArray();

The distinction between the first and the second is the useful one. A plain foreach hides pagination details and gives you only the current page, which is the right behaviour when you are rendering a list. The lazy() variant fetches the next cursor on demand, so a script that needs to walk every server in a large organisation does not have to hold the whole set in memory and does not make a request per page up front.

Cursor pagination rather than offset pagination is the right choice for an API that is changing underneath you, because a cursor is anchored to a position rather than to a count, so a server created while you are iterating does not shift the pages under your feet.

Resources themselves are simple value objects. Each one is an instance of a class such as Laravel\Forge\Resources\Server, with public properties including $name, $id, $size and $region, so the data is read directly rather than through getter calls.

Waiting for a site to finish installing, and the timeout that guards it

Some Forge operations are asynchronous, and the SDK offers to block on them for you.

php
// This will wait until the site is fully installed (max 30 seconds)
$site = $forge->createSite($organizationSlug, $serverId, [
    'domain' => 'example.com',
    'type' => 'php',
]);

The default is a 30 second wait, and the site is only returned once Forge reports it fully installed. That is a real convenience for a provisioning script, because a create followed immediately by a deployment would otherwise race the installation.

You can turn the waiting off by passing false as the last argument:

php
// Don't wait
$site = $forge->createSite($organizationSlug, $serverId, $data, false);

And you can change the limit:

php
// Wait up to 2 minutes
$site = $forge->setTimeout(120)->createSite($organizationSlug, $serverId, $data);

The setTimeout call is a fluent setter on the client, so the timeout is per call rather than global, which is the right granularity when one operation in a script is slow and the rest are not.

The failure mode is explicit. If waiting exceeds the timeout, a Laravel\Forge\Exceptions\TimeoutException will be thrown. That is a dedicated exception type, so a caller can catch the timeout specifically and distinguish it from a transport failure, which matters when the script's job is to clean up a half-provisioned server.

The rest of the resource surface, and what is left to the Forge dashboard

The SDK covers the resources a Forge user touches day to day, and the method names follow a consistent pattern.

For organizations there is organizations() to list them, organization() to fetch one, and serverCredentials() and serverCredential() to read the credentials attached to an organization.

For sites the pattern is sites() to list all, organizationSites() for a whole organization, serverSites() for one server, and organizationSite() for a single site, alongside createSite, updateSite and deleteSite.

Domains and certificates are handled in the same shape, with domains(), createDomain, updateDomain and deleteDomain, plus domainCertificates() to list them, activeDomainCertificate() to fetch the live one, and certificate(), createCertificate and deleteCertificate for individual certificates.

Deployments have their own group: webhooks() and createWebhook for the deployment hooks, deploymentScript() and updateDeploymentScript for the script a site runs, createDeployment() to trigger one, deploymentStatus() to read where it got to, updateDeploymentState() to change it, and createPushToDeploy() and a delete counterpart for the push to deploy feature.

What the SDK does not do is anything with the content of your application. It is a control plane client for the infrastructure service, so a deployment script is a string you set and a domain is a name you register, but there is no file upload, no database query interface and no application-level operation. If you need that, you are writing application code and running it on the server, and the SDK has no part in it.

The repository is a conventional modern PHP package: a src/ directory, tests/ with both a phpunit.xml.dist and a phpunit-integration.xml, a phpstan.neon.dist for static analysis, a .editorconfig, composer.json, CHANGELOG.md, and the two upgrade guides. The licence is MIT, and the project is published on Packagist as laravel/forge-sdk.

Editorial conclusion

laravel-forge-sdk suits a team provisioning Forge servers from PHP, because it covers the full resource surface from servers and sites through domains, certificates and deployments, and the cursor paginator means listing a large fleet does not require reading every page into memory. It does not suit you if you are on API v1, because version 4 is a breaking change and every call needs an organization slug. Before you upgrade, work through the UPGRADE-4.0.md guide that the repository includes, and note the package tracks Forge itself, so the SDK version and the API version are coupled and a Forge-side change can break your client.

Frequently asked questions

How do I install and instantiate the Laravel Forge SDK?

Run composer require laravel/forge-sdk, then create the client with $forge = new Laravel\Forge\Forge($apiToken);. The SDK is published on Packagist as laravel/forge-sdk and is MIT licensed.

What changed between version 3 and version 4 of the Forge SDK?

Version 4.0 uses the Forge API v2 and introduces significant breaking changes, because all endpoints now require an organization slug as the first parameter. The repository includes an UPGRADE-4.0.md guide with detailed migration instructions, and the default branch is named 4.x.

How do I get the organization slug the Forge SDK requires?

Call $forge->organizations(), which returns the organizations you have access to, and read the slug property from one of them, for example $organizationSlug = $organizations[0]->slug;.

How do I control how long the Forge SDK waits for an async operation?

The default is a 30 second wait for something like createSite. Pass false as the last argument to skip waiting, or use $forge->setTimeout(120)->createSite(...) to wait up to two minutes. If the wait exceeds the timeout, a Laravel\Forge\Exceptions\TimeoutException is thrown.

Official sources

  1. laravel/forge-sdk on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/laravel-forge-sdk.svg)](https://hysenlabs.com/projects/laravel-forge-sdk)

Was this useful?