Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Dropbox Api Laravel Package

spatie/dropbox-api

A minimal PHP client for Dropbox API v2, providing just the methods needed for the flysystem-dropbox adapter. Create folders, list contents, and generate temporary links with a simple authorization token.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a thin Guzzle wrapper around Dropbox API v2 — not a Laravel package. There is no service provider, no config file, no facade, and no HTTP-client integration with Illuminate\Support\Facades\Http. Plan for ~15 minutes to wire it in yourself.

1. Install and get a token

composer require spatie/dropbox-api

Generate an access token in the Dropbox App Console and put it in .env:

DROPBOX_TOKEN=sl.xxxxxxxxxxxxxxxxxxxx

2. Bind the client once

// config/services.php
'dropbox' => ['token' => env('DROPBOX_TOKEN')],

// app/Providers/AppServiceProvider.php — register()
$this->app->singleton(\Spatie\Dropbox\Client::class, fn () => new \Spatie\Dropbox\Client(
    config('services.dropbox.token')
));

3. First use case: ship a generated artifact and hand back a link

The most common day-one need is "put this export/file in Dropbox and give me a URL." That's two calls:

$client = app(\Spatie\Dropbox\Client::class);

$client->upload('/exports/invoices-2026-01.csv', $csv, 'overwrite');
$url = $client->getTemporaryLink('/exports/invoices-2026-01.csv')['link'];

Where to look first: the README's "Dropbox Endpoints" section explicitly tells you to read src/Client.php — do that. The public method list is the documentation for this package, and it's short. Methods you'll actually reach for: upload, download, createFolder, listFolder / listFolderContinue, move, copy, delete, getMetadata, getTemporaryLink, listSharedLinks, createSharedLinkWithSettings, revokeToken, getAccountInfo, plus the chunked-upload session methods.

Know when to not use it directly: this package is the transport underneath spatie/flysystem-dropbox. If what you want is a Storage::disk('dropbox') that behaves like any other Laravel disk, install the Flysystem adapter and reach for Client only for the operations Flysystem doesn't model.

Implementation Patterns

Per-user tokens via TokenProvider (the pattern you'll need in production)

Since Dropbox moved to short-lived access tokens, a single static DROPBOX_TOKEN env var is only viable for a single-tenant app. For multi-tenant, implement the interface:

use Spatie\Dropbox\TokenProvider;

final class AccountDropboxToken implements TokenProvider
{
    public function __construct(private DropboxAccount $account) {}

    public function getToken(): string
    {
        return Cache::remember(
            "dropbox:access:{$this->account->id}",
            now()->addMinutes(50),           // tokens live ~4h; refresh early
            fn () => $this->account->refreshAccessToken(), // oauth2-client, refresh_token grant
        );
    }
}

$client = new Client(new AccountDropboxToken($account));

Because the client resolves the token through the provider on each request, expiration is handled at the boundary. App-only auth (new Client([$appKey, $appSecret])) exists for app-folder-scoped work and no-auth construction works for public endpoints — both landed in 1.14.0.

Push uploads onto the queue, always

Uploads are network-bound and flaky. Never do them inline in a request:

final class UploadExport implements ShouldQueue
{
    public int $tries = 5;
    public array $backoff = [10, 60, 300];

    public function __construct(public int $exportId) {}

    public function handle(Client $client): void
    {
        $export = Export::findOrFail($this->exportId);

        // 'add' | 'overwrite' | 'update' (update requires a rev)
        $client->upload("/exports/{$export->filename}", $export->contents(), 'overwrite');
    }

    public function failed(Throwable $e): void { /* mark export as failed */ }
}

upload() accepts a string body and, since 1.20.2, a stream resource — so you can pass an open file handle instead of reading a 200 MB file into memory.

Large files: uploadChunked()

Single-call upload caps out at 150 MB. For anything approaching that, use the chunked path:

$stream = fopen(storage_path("app/{$file}"), 'r');
$client->uploadChunked("/backups/{$file}", $stream);

For resumable/retryable behaviour across job attempts, drive the session manually with uploadSessionStart() → uploadSessionAppend() → uploadSessionFinish() and persist the session_id between attempts. Note 1.25.0 fixed an infinite loop in uploadChunked() when the transport didn't read past stream end — pin to ^1.25 if you're chunking streams.

Idempotent folder provisioning

Dropbox returns a conflict if the folder exists, so treat conflict as success:

try {
    $client->createFolder($path);
} catch (\Spatie\Dropbox\Exceptions\BadRequest $e) {
    // already exists — fine
}

Serving files: temporary links vs. shared links

getTemporaryLink() returns a URL valid for ~4 hours. Generate it on demand, never store it. If you need a durable URL that survives, use createSharedLinkWithSettings() (or listSharedLinks() to reuse an existing one), and revoke via the Dropbox UI / API when the relationship ends.

Escape hatch: rpcEndpointRequest / contentEndpointRequest

The package is deliberately minimal; anything not wrapped is one call away. This is the intended extension point, not a hack:

// search
$client->rpcEndpointRequest('search', ['path' => '', 'query' => 'bat cave']);

// thumbnail batch — note the subdomain prefix
$client->rpcEndpointRequest('content::files/get_thumbnail_batch', $parameters);

The subdomain:: prefix is the documented way to hit the content host for RPC-style endpoints that live there. Anything you can express as a JSON body goes through rpcEndpointRequest; anything with a binary body goes through contentEndpointRequest.

Listing with pagination

listFolder() returns entries, cursor, and hasMore. Loop with listFolderContinue($cursor) until hasMore is false — Dropbox caps page size, so an un-looped listFolder silently truncates large directories.

Testing

Http::fake() will not intercept this client — it constructs its own Guzzle stack. Two viable approaches:

// A) mock the concrete client
$client = Mockery::mock(Client::class);
$client->shouldReceive('upload')->once()->with('/exports/x.csv', Mockery::any(), 'overwrite');
$this->app->instance(Client::class, $client);

// B) better long-term: define your own DropboxStorage interface,
//    implement it with Spatie's Client, and mock the interface.

Option B costs 20 lines and decouples your domain from a third-party concrete class you can't otherwise intercept.

Gotchas and Tips

  • No retry logic, no rate-limit awareness. Dropbox returns 429 with a Retry-After header. Laravel's retry() helper doesn't read that header. Catch the exception in your job and $this->release($seconds) accordingly, or you'll hammer the API and get throttled harder.
  • Conflicts are exceptions, not booleans. upload() and move() throw BadRequest on collision with messages containing things like to/conflict/file/... Since 1.3.0 the exception carries the Dropbox error code — branch on that, not on string matching, when you can. Both methods accept an autorename flag (1.18.0 / 1.19.0): $client->move($from, $to, autorename: true) yields somefile (1).txt. Use it when a duplicate is acceptable; use overwrite mode when it isn't.
  • The client is stateful. It holds the access token, and since 1.21.0 it can also hold a namespace ID (needed when acting inside a Dropbox Business team space). Binding it as a global singleton is fine for one token; for per-user tokens bind it scoped() or instantiate it per job, otherwise you'll leak one tenant's token into another's request under Octane.
  • Path normalization is opinionated. Dropbox paths are case-insensitive and must lead with /. The client's path normalizer also permits id:, rev:, and ns: prefixed references (1.6.5) — useful for referring to a file that has been renamed underneath you.
  • Temporary links expire (~4h). Don't cache them in the database or paste them into emails that sit in a queue overnight. Mint them at render time.
  • getTemporaryLink needs the file to exist and to be a file, not a folder — folders get no temp link; use a shared link on the folder instead.
  • Chunk sizes must be 4 MB multiples except the final chunk. The library has handled max-chunk sizing internally since 1.6.4, but if you drive sessions manually, get this wrong and you'll get opaque BadRequests.
  • Empty-content sessions are allowed only from 1.24.1+, and uploadChunked() stream handling was broken before 1.25.0. If your composer constraint floats down to ^1.20, you'll hit both bugs. Pin to a recent minor.
  • revokeToken() when a user disconnects, but be aware it kills every token for that app/user pair — not just the one you hold. Revoke before dropping the record, or you'll orphan a live grant.
  • Webhooks aren't implemented. Dropbox's notification webhook requires HMAC verification with your app secret; you'll hand-roll that controller. You can then use listFolderContinue / files/list_folder/longpoll via rpcEndpointRequest to fetch changes.
  • listFolder does not recurse. Set recursive => true in the arguments array, or you'll get one level and wonder where the files went.
  • This library will not grow for you. 307 stars, minimal maintained surface, and the maintainers are explicit: it contains "only the methods needed for our flysystem-dropbox adapter." Treat rpcEndpointRequest as a first-class API, and wrap it in your own small repository class so your codebase has one place to change when you need a new endpoint.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route