BridgeKit Documentation
A Laravel library for third-party integrations — OAuth providers, credential storage (S3, FTP, SFTP), SharePoint, webhooks, and a single FileStorageInterface including recursive listTree().
Installation
Require the package via Composer:
composer require bridgekit-tools/bridgekit-lib
The service provider and facade are auto-discovered. To publish the configuration file:
php artisan vendor:publish --tag=bridgekit-config
Configuration
Add your provider credentials to .env:
# Google
BRIDGEKIT_GOOGLE_CLIENT_ID=your-client-id
BRIDGEKIT_GOOGLE_CLIENT_SECRET=your-client-secret
BRIDGEKIT_GOOGLE_REDIRECT_URI=https://app.test/callback/google
# Microsoft
BRIDGEKIT_MICROSOFT_CLIENT_ID=
BRIDGEKIT_MICROSOFT_CLIENT_SECRET=
BRIDGEKIT_MICROSOFT_REDIRECT_URI=
BRIDGEKIT_MICROSOFT_TENANT=common
# SharePoint (Microsoft Graph — site library; optional if you only use OneDrive)
BRIDGEKIT_SHAREPOINT_SITE_ID=
# Or resolve by path, e.g. /contoso.sharepoint.com:/sites/marketing
BRIDGEKIT_SHAREPOINT_SITE_PATH=
BRIDGEKIT_SHAREPOINT_DRIVE_ID=
# Meta (Facebook)
BRIDGEKIT_META_CLIENT_ID=
BRIDGEKIT_META_CLIENT_SECRET=
BRIDGEKIT_META_REDIRECT_URI=
BRIDGEKIT_META_GRAPH_VERSION=v21.0
# LinkedIn
BRIDGEKIT_LINKEDIN_CLIENT_ID=
BRIDGEKIT_LINKEDIN_CLIENT_SECRET=
BRIDGEKIT_LINKEDIN_REDIRECT_URI=
# X (Twitter)
BRIDGEKIT_X_CLIENT_ID=
BRIDGEKIT_X_CLIENT_SECRET=
BRIDGEKIT_X_REDIRECT_URI=
# Dropbox
BRIDGEKIT_DROPBOX_CLIENT_ID=
BRIDGEKIT_DROPBOX_CLIENT_SECRET=
BRIDGEKIT_DROPBOX_REDIRECT_URI=
# FTP
BRIDGEKIT_FTP_HOST=ftp.example.com
BRIDGEKIT_FTP_PORT=21
BRIDGEKIT_FTP_USERNAME=
BRIDGEKIT_FTP_PASSWORD=
BRIDGEKIT_FTP_SSL=false
BRIDGEKIT_FTP_PASSIVE=true
BRIDGEKIT_FTP_ROOT=/
# S3 (AWS or S3-compatible: MinIO, DigitalOcean Spaces, etc.)
BRIDGEKIT_S3_KEY=
BRIDGEKIT_S3_SECRET=
BRIDGEKIT_S3_REGION=us-east-1
BRIDGEKIT_S3_BUCKET=my-bucket
BRIDGEKIT_S3_ENDPOINT=
# SFTP
BRIDGEKIT_SFTP_HOST=sftp.example.com
BRIDGEKIT_SFTP_PORT=22
BRIDGEKIT_SFTP_USERNAME=
BRIDGEKIT_SFTP_PASSWORD=
BRIDGEKIT_SFTP_PRIVATE_KEY=
BRIDGEKIT_SFTP_ROOT=/
The config file at config/bridgekit.php maps each of these to its provider section.
Quick Start
Here's a minimal example — authenticate a user via Google OAuth then list their Drive files:
use BridgeKit\Facades\BridgeKit;
// 1. Redirect to Google consent screen
$url = BridgeKit::google()
->auth()
->getAuthorizationUrl(['https://www.googleapis.com/auth/drive.readonly']);
return redirect($url);
// 2. Handle callback
$token = BridgeKit::google()
->auth()
->handleCallback($request->code);
// 3. Use the token
$files = BridgeKit::google()
->setToken($token)
->drive()
->listFiles();
Architecture
BridgeKit follows a layered architecture:
ConnectManager → resolves providers by name
├─ AbstractProvider → OAuth providers (token + config)
│ └─ Services → Drive, Gmail, Calendar, Posts...
└─ AbstractStorageProvider → credential-based providers (FTP, S3, SFTP)
└─ StorageService → implements FileStorageInterface
Contracts/ → interfaces each service implements
DTOs/ → readonly typed value objects
Enums/ → Provider, Visibility, MediaType, etc.
Key design decisions
- Two provider base classes —
AbstractProviderfor OAuth flows,AbstractStorageProviderfor credential-based storage - Services are lazy singletons — resolved on first access, cached until token changes
- DTOs are immutable —
final readonly classwithJsonSerializable - Unified FileStorageInterface — same API across Google Drive, OneDrive, SharePoint, Dropbox, FTP, S3, SFTP, including
listTree()for recursive folder trees - S3 uses native AWS Signature V4 — zero external dependencies, works with any S3-compatible endpoint
Providers
Each provider is an entry point that holds the OAuth token and exposes its services:
use BridgeKit\Facades\BridgeKit;
use BridgeKit\Enums\Provider;
// Via the facade
$google = BridgeKit::google();
$microsoft = BridgeKit::microsoft();
// Via the enum
$provider = BridgeKit::provider(Provider::LinkedIn);
// With custom config (bypasses config/bridgekit.php)
$custom = BridgeKit::google([
'client_id' => 'custom-id',
'client_secret' => 'custom-secret',
]);
Setting the token
Every service call requires an authenticated token. Call setToken() once on the provider:
$google = BridgeKit::google()->setToken($token);
// All services share the same token
$google->drive()->listFiles();
$google->gmail()->send($email);
$google->calendar()->listEvents();
When you call setToken(), the service cache is flushed — new service instances will use the new token.
Available services per provider
| Provider | Auth | Services | Contracts |
|---|---|---|---|
| OAuth 2.0 | drive(), gmail(), calendar(), webhooks() | FileStorage, EmailSender, Calendar, Webhook | |
| Microsoft | OAuth 2.0 | onedrive(), sharepoint(), outlook(), calendar(), webhooks() | FileStorage (×2), EmailSender, Calendar, Webhook |
| Meta | OAuth 2.0 | posts(), webhooks() | PostPublisher, Webhook |
| OAuth 2.0 | posts() | PostPublisher | |
| X | OAuth 2.0 | posts(), webhooks() | PostPublisher, Webhook |
| Dropbox | OAuth 2.0 | storage() | FileStorage |
| FTP | Credentials | storage() | FileStorage |
| S3 | Credentials | storage() | FileStorage |
| SFTP | Credentials | storage() | FileStorage |
ConnectManager
The ConnectManager is the central singleton registered as app('bridgekit'):
$manager = app('bridgekit');
// Resolve by enum or string
$manager->provider(Provider::Google);
$manager->provider('google');
// Typed shortcuts — OAuth providers
$manager->google(); // → GoogleProvider
$manager->microsoft(); // → MicrosoftProvider
// Typed shortcuts — OAuth storage providers
$manager->dropbox(); // → DropboxProvider
// Typed shortcuts — Credential storage providers (no OAuth)
$manager->ftp(); // → FtpProvider
$manager->s3(); // → S3Provider
$manager->sftp(); // → SftpProvider
// Multi-posting across social providers
$manager->multiPost(); // → MultiPoster
// List all registered providers
$manager->getRegisteredProviders();
// Clear resolved instances
$manager->flush();
Enums
BridgeKit provides 6 backed string enums for type safety:
use BridgeKit\Enums\Provider; // Google, Microsoft, Meta, LinkedIn, X, Dropbox, Ftp, S3, Sftp
use BridgeKit\Enums\Visibility; // Public, Connections, Private
use BridgeKit\Enums\EventStatus; // Confirmed, Tentative, Cancelled
use BridgeKit\Enums\MailFolder; // Inbox, Sent, Drafts, Trash, Spam
use BridgeKit\Enums\OAuthGrantType; // AuthorizationCode, RefreshToken, ...
use BridgeKit\Enums\ServiceType; // Auth, Drive, OneDrive, Gmail, ..., Storage
use BridgeKit\Enums\MediaType; // Image, Video, Gif, Document
The Provider enum has helper methods to distinguish OAuth from credential-based providers:
Provider::Google->requiresOAuth(); // true
Provider::Ftp->requiresOAuth(); // false
Provider::S3->isStorageOnly(); // true
These are used throughout the DTOs and services. For example:
use BridgeKit\DTOs\SocialPost;
use BridgeKit\Enums\Visibility;
$post = new SocialPost(
content: 'Hello from BridgeKit!',
visibility: Visibility::Public,
);
Google Drive
Implements FileStorageInterface. Supports Drive API v3 with shared drives.
$drive = BridgeKit::google()->setToken($token)->drive();
// List files in root
$files = $drive->listFiles();
// List files in a folder
$files = $drive->listFiles('folder-id-here');
// Get file metadata
$file = $drive->getFile('file-id');
echo $file->name; // StorageFile DTO
echo $file->mimeType;
echo $file->size;
// Upload a small file
$file = $drive->uploadFile('notes.txt', 'Hello world', 'text/plain');
// Create a folder
$folder = $drive->createFolder('Reports', 'parent-folder-id');
// Search
$results = $drive->searchFiles('quarterly report');
// Delete
$drive->deleteFile('file-id');
OneDrive
Implements the same FileStorageInterface via Microsoft Graph API:
$onedrive = BridgeKit::microsoft()->setToken($token)->onedrive();
// Same API as Google Drive
$files = $onedrive->listFiles();
$file = $onedrive->uploadFile('doc.pdf', $content, 'application/pdf');
$onedrive->downloadFile('item-id');
SharePoint
Document libraries in SharePoint Online are exposed through Microsoft Graph with the same patterns as OneDrive. Use either a full site ID or a site_path that Graph can resolve. Optional drive_id targets a specific library; otherwise the default site drive is used.
$sp = BridgeKit::microsoft()
->setToken($token)
->sharepoint([
'site_path' => '/contoso.sharepoint.com:/sites/marketing',
// 'drive_id' => 'b!xxx', // optional
]);
$files = $sp->listFiles();
$tree = $sp->listTree('', ['max_depth' => 4]);
$libs = $sp->listLibraries(); // enumerate libraries on the site
Required delegated scopes (examples): Sites.Read.All or Sites.ReadWrite.All, plus Files.ReadWrite.All for uploads — align with your app registration.
Folder tree (listTree)
Every implementation of FileStorageInterface provides listTree(string $folderId = '', array $options = []): StorageTreeNode. The tree is built on top of listFilesLazy() via the BuildsFileTree trait, so behaviour is consistent across Drive, OneDrive, SharePoint, Dropbox, S3, FTP, and SFTP.
use BridgeKit\Facades\BridgeKit;
$storage = BridgeKit::s3()->storage();
$root = $storage->listTree('uploads/', [
'max_depth' => 5,
'include_files' => true,
'root_name' => 'uploads',
]);
echo $root->toAscii(); // Unix tree-style string for logs / UI
echo $root->countDescendants(); // folders + files below root
echo $root->totalSize(); // cumulative bytes
foreach ($root->walk() as $node) {
if ($node->isFile()) {
echo $node->file->webUrl;
}
}
// JSON API responses
return response()->json($root);
The StorageTreeNode DTO wraps a StorageFile plus child nodes, depth, walk(), toAscii(), and implements JsonSerializable.
Streaming & Large Files
For large files, BridgeKit provides zero-memory-copy operations:
Streaming download
Returns a PHP stream resource — the file is never loaded into memory:
$stream = $drive->downloadStream('file-id');
// Copy to local disk without loading into memory
$out = fopen('/tmp/large-file.zip', 'wb');
stream_copy_to_stream($stream, $out);
fclose($out);
fclose($stream);
Resumable chunked upload
Uploads files of any size using resumable protocols (Google) or upload sessions (OneDrive):
// From a file path
$file = $drive->uploadLargeFile(
name: 'backup.zip',
filePathOrStream: '/path/to/backup.zip',
mimeType: 'application/zip',
chunkSize: 10 * 1024 * 1024, // 10 MiB chunks
);
// From an open stream
$stream = fopen('/path/to/video.mp4', 'rb');
$file = $onedrive->uploadLargeFile(
name: 'video.mp4',
filePathOrStream: $stream,
mimeType: 'video/mp4',
);
Lazy listing with auto-pagination
Iterates through all pages via a PHP Generator — constant memory regardless of file count:
// Process 10,000+ files without loading them all into memory
foreach ($drive->listFilesLazy() as $file) {
echo $file->name . "\n";
}
// Collect all files (auto-paginates)
$allFiles = $drive->listFiles(); // uses listFilesLazy internally
Memory comparison
| Operation | Without streaming | With streaming |
|---|---|---|
| Download 500 MB | 500 MB RAM | ~8 KB (stream buffer) |
| Upload 1 GB | 1 GB RAM | 5 MB (chunk size) |
| List 10,000 files | All in memory | 1 file at a time |
FTP Storage
Connect to FTP/FTPS servers. No OAuth needed — uses host credentials directly:
// Via config (reads from .env / config/bridgekit.php)
$ftp = BridgeKit::ftp()->storage();
// Or with inline config
$ftp = BridgeKit::ftp([
'host' => 'ftp.example.com',
'port' => 21,
'username' => 'user',
'password' => 'secret',
'ssl' => true,
'passive' => true,
'root' => '/uploads',
])->storage();
// Same FileStorageInterface as Google Drive / OneDrive
$files = $ftp->listFiles('/reports');
$ftp->uploadFile('data.csv', $csvContent, 'text/csv', '/reports');
$content = $ftp->downloadFile('/reports/data.csv');
$ftp->deleteFile('/reports/old.csv');
$ftp->createFolder('archive', '/reports');
FTPS (FTP over SSL)
Set BRIDGEKIT_FTP_SSL=true to use ftp_ssl_connect() for encrypted connections.
S3 Storage
Works with AWS S3 and any S3-compatible API (MinIO, DigitalOcean Spaces, Backblaze B2, Cloudflare R2):
// Via config
$s3 = BridgeKit::s3()->storage();
// Or with inline config
$s3 = BridgeKit::s3([
'key' => 'AKIAIOSFODNN7EXAMPLE',
'secret' => 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
'region' => 'eu-west-1',
'bucket' => 'my-bucket',
])->storage();
// Standard FileStorageInterface
$files = $s3->listFiles('uploads/images');
$s3->uploadFile('photo.jpg', $binary, 'image/jpeg', 'uploads/images');
$content = $s3->downloadFile('uploads/images/photo.jpg');
$s3->deleteFile('uploads/images/old.jpg');
$s3->createFolder('backups');
Public object URLs (webUrl)
Each StorageFile from S3 listing, getFile(), uploads, and folder creation includes webUrl when applicable: virtual-hosted style on AWS (https://{bucket}.s3.{region}.amazonaws.com/{key}), or path-style when a custom endpoint is set (MinIO, R2, DigitalOcean Spaces, etc.). Keys are URL-encoded per segment.
$file = $s3->getFile('reports/summary.pdf');
echo $file->webUrl;
// Time-limited URL for private buckets (S3 concrete service only — not on the interface)
/** @var \BridgeKit\Providers\S3\Services\S3StorageService $s3 */
$s3 = BridgeKit::s3()->storage();
$url = $s3->getPresignedUrl('private/doc.pdf', expiresIn: 900);
S3-compatible endpoints
For non-AWS services, set the endpoint config:
# MinIO
BRIDGEKIT_S3_ENDPOINT=http://localhost:9000
# DigitalOcean Spaces
BRIDGEKIT_S3_ENDPOINT=https://nyc3.digitaloceanspaces.com
# Cloudflare R2
BRIDGEKIT_S3_ENDPOINT=https://<account-id>.r2.cloudflarestorage.com
Multipart upload for large files
S3 multipart uploads are handled automatically by uploadLargeFile():
$s3->uploadLargeFile(
name: 'database-backup.sql.gz',
filePathOrStream: '/tmp/backup.sql.gz',
mimeType: 'application/gzip',
folderId: 'backups/daily',
chunkSize: 10 * 1024 * 1024, // 10 MB parts
);
SFTP Storage
SSH File Transfer Protocol with password or public key authentication:
// Via config
$sftp = BridgeKit::sftp()->storage();
// With inline config — password auth
$sftp = BridgeKit::sftp([
'host' => 'server.example.com',
'port' => 22,
'username' => 'deploy',
'password' => 'secret',
'root' => '/var/www/uploads',
])->storage();
// With public key auth
$sftp = BridgeKit::sftp([
'host' => 'server.example.com',
'username' => 'deploy',
'private_key' => '/home/user/.ssh/id_rsa',
'passphrase' => 'optional-passphrase',
])->storage();
// Same API as all storage providers
$files = $sftp->listFiles('/var/www/uploads');
$sftp->uploadFile('deploy.tar.gz', $content, '', '/var/www/releases');
$sftp->downloadStream('/var/log/app.log');
Unified storage interface
All credential and OAuth storage backends (Google Drive, OneDrive, SharePoint, Dropbox, FTP, S3, SFTP) implement the same FileStorageInterface, including listTree(). Switch providers by changing one line:
use BridgeKit\Contracts\Storage\FileStorageInterface;
function backupTo(FileStorageInterface $storage): void
{
$storage->uploadLargeFile(
name: 'backup-' . date('Y-m-d') . '.sql.gz',
filePathOrStream: '/tmp/backup.sql.gz',
);
}
// Works with any provider:
backupTo(BridgeKit::s3()->storage());
backupTo(BridgeKit::ftp()->storage());
backupTo(BridgeKit::sftp()->storage());
backupTo(BridgeKit::google()->setToken($token)->drive());
Meta / Facebook
Publish to Facebook Pages via the Graph API:
use BridgeKit\DTOs\SocialPost;
use BridgeKit\Enums\Visibility;
$meta = BridgeKit::meta()->setToken($token);
$result = $meta->posts()->publish(new SocialPost(
content: 'New product launch!',
mediaUrls: ['https://example.com/photo.jpg'],
visibility: Visibility::Public,
metadata: ['page_id' => 'your-page-id'],
));
echo $result->id; // Post ID
echo $result->url; // Permalink
Publish UGC posts via the LinkedIn API v2:
$linkedin = BridgeKit::linkedin()->setToken($token);
$result = $linkedin->posts()->publish(new SocialPost(
content: 'Exciting company update!',
visibility: Visibility::Public,
metadata: ['author_urn' => 'urn:li:person:xxx'],
));
// Manage posts
$post = $linkedin->posts()->getPost($result->id);
$linkedin->posts()->deletePost($result->id);
X / Twitter
Publish tweets via the X API v2 (OAuth 2.0 with PKCE):
$x = BridgeKit::x()->setToken($token);
$result = $x->posts()->publish(new SocialPost(
content: 'Hello from BridgeKit! 🚀',
));
echo $result->url; // https://x.com/i/web/status/...
Gmail
Send and manage emails via the Gmail API:
use BridgeKit\DTOs\EmailMessage;
$gmail = BridgeKit::google()->setToken($token)->gmail();
// Send an email
$messageId = $gmail->send(new EmailMessage(
subject: 'Welcome to BridgeKit',
body: '<h1>Hello!</h1><p>Welcome aboard.</p>',
to: ['user@example.com'],
isHtml: true,
));
// List messages
$messages = $gmail->listMessages(MailFolder::Inbox, limit: 20);
// Read a message
$msg = $gmail->getMessage($messageId);
echo $msg->subject;
echo $msg->from;
Outlook
Same EmailSenderInterface via Microsoft Graph:
$outlook = BridgeKit::microsoft()->setToken($token)->outlook();
$outlook->send(new EmailMessage(
subject: 'Report attached',
body: 'Please find the report.',
to: ['boss@company.com'],
));
$messages = $outlook->listMessages('INBOX', 50);
Google Calendar
Implements CalendarInterface with typed CalendarEvent DTOs:
use BridgeKit\DTOs\CalendarEvent;
$calendar = BridgeKit::google()->setToken($token)->calendar();
// List events
$events = $calendar->listEvents('primary', [
'timeMin' => now()->toRfc3339String(),
'maxResults' => 10,
]);
// Create an event
$event = $calendar->createEvent(new CalendarEvent(
title: 'Team Standup',
description: 'Daily sync',
startAt: new DateTimeImmutable('2026-04-01T09:00:00'),
endAt: new DateTimeImmutable('2026-04-01T09:30:00'),
timezone: 'Europe/Paris',
attendees: ['alice@company.com', 'bob@company.com'],
));
echo $event->id;
echo $event->webUrl;
Microsoft Calendar
Same CalendarInterface via Microsoft Graph API:
$calendar = BridgeKit::microsoft()->setToken($token)->calendar();
$events = $calendar->listEvents();
$event = $calendar->createEvent(new CalendarEvent(
title: 'Sprint Review',
startAt: new DateTimeImmutable('2026-04-05T14:00:00'),
endAt: new DateTimeImmutable('2026-04-05T15:00:00'),
timezone: 'America/New_York',
location: 'Conference Room B',
));
$calendar->deleteEvent($event->id);
Webhooks
BridgeKit provides a unified webhook system that receives notifications from providers and dispatches Laravel events. When a file is moved on Google Drive, a post gets a comment on Meta, or a calendar event is updated — your app reacts automatically.
How it works
Provider (Google, Meta, X...)
→ POST /webhooks/bridgekit/{provider}
→ WebhookController
→ verify signature
→ parse into WebhookPayload
→ WebhookProcessor dispatches Laravel Events
→ WebhookReceived (always)
→ FileCreated / FileMoved / PostPublished / ... (specific)
Setup
The webhook route is registered automatically at /webhooks/bridgekit/{provider}. Configure in config/bridgekit.php:
'webhooks' => [
'enabled' => true,
'path' => 'webhooks/bridgekit', // URL prefix
'middleware' => [], // add middleware if needed
],
Listening to events
Register listeners in your EventServiceProvider or use closures:
use BridgeKit\Events\Storage\FileMoved;
use BridgeKit\Events\Storage\FileDeleted;
use BridgeKit\Events\Social\CommentReceived;
use BridgeKit\Events\Calendar\CalendarEventUpdated;
use BridgeKit\Events\WebhookReceived;
// Specific event — file was moved to another folder
Event::listen(FileMoved::class, function (FileMoved $event) {
$payload = $event->payload;
Log::info("File {$payload->resourceId} moved", [
'from' => $event->fromFolder(),
'to' => $event->toFolder(),
'provider' => $payload->provider->value,
]);
// Sync the change in your app
FileRecord::where('external_id', $payload->resourceId)
->update(['folder_id' => $event->toFolder()]);
});
// Catch-all — every webhook from every provider
Event::listen(WebhookReceived::class, function (WebhookReceived $event) {
WebhookLog::create($event->payload->jsonSerialize());
});
Available events
| Category | Event class | Triggered when |
|---|---|---|
| Storage | FileCreated | New file uploaded |
| Storage | FileUpdated | File content or metadata changed |
| Storage | FileDeleted | File permanently deleted |
| Storage | FileMoved | File moved to a different folder |
| Storage | FileTrashed | File moved to trash |
| Social | PostPublished | New post created |
| Social | PostDeleted | Post removed |
| Social | CommentReceived | New comment on a post |
| Social | EngagementReceived | Reaction, mention, follow change |
| Calendar | CalendarEventCreated | New calendar event |
| Calendar | CalendarEventUpdated | Event time/details changed |
| Calendar | CalendarEventCancelled | Event cancelled |
| Generic | WebhookReceived | Every webhook (catch-all) |
Subscribing to provider webhooks
Each provider's webhooks() service lets you register (subscribe) and manage webhook subscriptions:
Google Drive — watch for file changes
$google = BridgeKit::google()->setToken($token);
// Watch all Drive changes
$reg = $google->webhooks()->subscribe(
callbackUrl: 'https://app.com/webhooks/bridgekit/google',
options: ['type' => 'drive', 'ttl' => 86400],
);
// Watch a specific file
$reg = $google->webhooks()->subscribe(
callbackUrl: 'https://app.com/webhooks/bridgekit/google',
options: ['resource_id' => 'file-id-here'],
);
// Watch calendar events
$reg = $google->webhooks()->subscribe(
callbackUrl: 'https://app.com/webhooks/bridgekit/google',
options: ['type' => 'calendar', 'calendar_id' => 'primary'],
);
// Unsubscribe
$google->webhooks()->unsubscribe($reg->id);
Microsoft Graph — subscriptions
$ms = BridgeKit::microsoft()->setToken($token);
// Watch OneDrive root for changes
$reg = $ms->webhooks()->subscribe(
callbackUrl: 'https://app.com/webhooks/bridgekit/microsoft',
options: ['resource' => 'me/drive/root', 'change_type' => 'updated'],
);
// Watch calendar
$reg = $ms->webhooks()->subscribe(
callbackUrl: 'https://app.com/webhooks/bridgekit/microsoft',
options: ['resource' => 'me/events', 'change_type' => 'created,updated,deleted'],
);
// Renew before expiration
$ms->webhooks()->renew($reg->id, ttlMinutes: 4230);
Meta — Page webhooks
$meta = BridgeKit::meta()->setToken($token);
// Get the verify token to configure in Meta App Dashboard
$reg = $meta->webhooks()->subscribe(
callbackUrl: 'https://app.com/webhooks/bridgekit/meta',
events: ['feed', 'mention', 'messages'],
);
// Use $reg->secret as the Verify Token in Meta Dashboard
X / Twitter — Account Activity
$x = BridgeKit::x()->setToken($token);
$reg = $x->webhooks()->subscribe(
callbackUrl: 'https://app.com/webhooks/bridgekit/x',
options: ['env_name' => 'production'],
);
The WebhookPayload DTO
Every webhook is parsed into a typed WebhookPayload:
$payload->provider; // Provider::Google
$payload->event; // WebhookEvent::FileMoved
$payload->resourceId; // 'abc123'
$payload->resourceType; // 'file'
$payload->data; // provider-specific data array
$payload->timestamp; // DateTimeImmutable
$payload->changes; // what changed (e.g. parent_from, parent_to)
$payload->raw; // original request body
// Helper methods
$payload->getChange('parent_from'); // 'old-folder-id'
$payload->hasChanges(); // true
Token Auto-Refresh
BridgeKit automatically refreshes expired OAuth tokens before each API call. A 60-second buffer ensures tokens are refreshed before actual expiration:
// Just use services normally — token refresh is automatic
$google = BridgeKit::google()->setToken($token);
// Even hours later, this works without manual refresh
$files = $google->drive()->listFiles();
// The token is refreshed transparently if expired.
// The provider's token is updated in place.
This works for all OAuth providers (Google, Microsoft, Meta, LinkedIn, X, Dropbox). Requires the token to have a valid refreshToken and expiresAt.
Retry & Rate Limiting
All HTTP requests include automatic retry with exponential backoff for transient errors (429, 500, 502, 503, 504):
// Default: 3 retries, 500ms base delay
$files = BridgeKit::google()
->setToken($token)
->drive()
->listFiles();
// Custom retry config on any service
$service = BridgeKit::google()->setToken($token)->drive();
$service->withRetry(maxRetries: 5, baseDelayMs: 1000);
Rate-limited responses throw RateLimitException with a retryAfter property:
use BridgeKit\Exceptions\RateLimitException;
try {
$result = $service->listFiles();
} catch (RateLimitException $e) {
$e->retryAfter; // seconds to wait
$e->provider; // 'google'
}
Multi-Posting
Broadcast a single post across multiple social providers in one call. Content is automatically adapted per platform (character limits, media count):
use BridgeKit\Facades\BridgeKit;
use BridgeKit\DTOs\SocialPost;
use BridgeKit\Enums\Provider;
$result = BridgeKit::multiPost()
->on(Provider::Meta, BridgeKit::meta()->setToken($metaToken)->posts())
->on(Provider::LinkedIn, BridgeKit::linkedin()->setToken($linkedInToken)->posts())
->on(Provider::X, BridgeKit::x()->setToken($xToken)->posts())
->publish(new SocialPost(
content: 'Exciting news! BridgeKit v1.2 is out with multi-posting support.'
));
// Check results
$result->isFullSuccess(); // true if all providers succeeded
$result->isPartialSuccess(); // true if some succeeded, some failed
$result->succeededProviders(); // ['meta', 'linkedin']
$result->failedProviders(); // ['x']
// Get per-provider details
$result->getResult('meta'); // SocialPostResult
$result->getError('x'); // Throwable
Platform limits
| Platform | Max characters | Max media |
|---|---|---|
| X (Twitter) | 280 | 4 |
| 3,000 | 9 | |
| Meta (Facebook) | 63,206 | 10 |
Content exceeding the limit is truncated with an ellipsis. Excess media is trimmed.
Dropbox Storage
Dropbox is a full OAuth provider with FileStorageInterface support:
use BridgeKit\Facades\BridgeKit;
// OAuth flow
$url = BridgeKit::dropbox()->auth()->getAuthorizationUrl([
'files.metadata.read',
'files.content.read',
'files.content.write',
]);
$token = BridgeKit::dropbox()->auth()->handleCallback($code);
// Use storage
$dropbox = BridgeKit::dropbox()->setToken($token);
$files = $dropbox->storage()->listFiles('/Documents');
$dropbox->storage()->uploadFile('report.pdf', $content, 'application/pdf', '/Documents');
$dropbox->storage()->createFolder('Projects', '/Documents');
$results = $dropbox->storage()->searchFiles('invoice');
Large file uploads (chunked sessions)
Files over 150 MB should use the upload session API:
$file = $dropbox->storage()->uploadLargeFile(
name: 'database-backup.sql.gz',
filePathOrStream: '/tmp/backup.sql.gz',
folderId: '/Backups',
chunkSize: 8 * 1024 * 1024 // 8 MB chunks
);
Custom Providers
Create your own provider by extending AbstractProvider (OAuth) or AbstractStorageProvider (credentials):
use BridgeKit\Support\AbstractProvider;
use BridgeKit\Contracts\Auth\OAuthInterface;
class DropboxProvider extends AbstractProvider
{
public function getName(): string
{
return 'dropbox';
}
public function auth(): OAuthInterface
{
return $this->resolveService('auth', fn () => new DropboxAuthService($this->config, $this));
}
public function storage(): DropboxStorageService
{
return $this->resolveService('storage', fn () => new DropboxStorageService($this));
}
public function getAvailableServices(): array
{
return [
'auth' => DropboxAuthService::class,
'storage' => DropboxStorageService::class,
];
}
}
For a credential-based storage provider, extend AbstractStorageProvider instead:
use BridgeKit\Support\AbstractStorageProvider;
use BridgeKit\Contracts\Storage\FileStorageInterface;
class WebDAVProvider extends AbstractStorageProvider
{
public function getName(): string
{
return 'webdav';
}
public function storage(): FileStorageInterface
{
return $this->resolveService('storage', fn () => new WebDAVStorageService($this->config));
}
public function getAvailableServices(): array
{
return ['storage' => WebDAVStorageService::class];
}
}
Register any custom provider in your AppServiceProvider:
use BridgeKit\Facades\BridgeKit;
public function boot(): void
{
BridgeKit::extend('dropbox', DropboxProvider::class);
BridgeKit::extend('webdav', WebDAVProvider::class);
}
Creating services with AbstractService
Extend AbstractService to get automatic token handling and HTTP client:
use BridgeKit\Support\AbstractService;
use BridgeKit\Contracts\Storage\FileStorageInterface;
class DropboxStorageService extends AbstractService implements FileStorageInterface
{
// getProviderName() and getAccessToken() are inherited
// authenticatedHttp() is ready to use
public function listFiles(string $folderId = '', array $options = []): array
{
$response = $this->authenticatedHttp()
->post('https://api.dropboxapi.com/2/files/list_folder', [
'path' => $folderId ?: '',
]);
return array_map(fn ($e) => $this->mapEntry($e), $response->json('entries'));
}
// ... implement remaining methods
}
Error Handling
BridgeKit throws typed exceptions for different error scenarios:
use BridgeKit\Exceptions\AuthenticationException;
use BridgeKit\Exceptions\ProviderException;
use BridgeKit\Exceptions\InvalidConfigException;
use BridgeKit\Exceptions\BridgeKitException;
try {
$files = BridgeKit::google()->drive()->listFiles();
} catch (AuthenticationException $e) {
// No token set, or token expired
} catch (ProviderException $e) {
// API error from the provider
echo $e->provider; // 'google'
echo $e->getCode(); // HTTP status code
} catch (InvalidConfigException $e) {
// Unknown provider or missing config
} catch (BridgeKitException $e) {
// Catch-all for any BridgeKit error
}
Exception hierarchy
BridgeKitException (extends RuntimeException)
├── AuthenticationException
├── ProviderException → has $provider property
└── InvalidConfigException