codepreneur / laravel-fathom
A Laravel SDK for the Fathom AI API.
Requires
- php: ^8.2
- illuminate/http: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
- nesbot/carbon: ^3.0
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.0
- orchestra/testbench: ^9.0|^10.0|^11.0
- phpunit/phpunit: ^11.5.50|^12.0|^13.0
README
A Laravel SDK for the Fathom AI API.
Open-source Laravel SDK for the Fathom AI API — meetings, transcripts, summaries and webhooks.
Features
- Laravel-native facade and service provider with auto-discovery
- Typed DTOs with forward-compatible
raw()access - Cursor pagination support
- Webhook signature verification and middleware
- Rate limit exception with header details
- Comprehensive offline test suite
- No database dependency — storage-agnostic SDK
Requirements
- PHP 8.2+ (PHP 8.3+ required for Laravel 13)
- Laravel 11, 12, or 13
Installation
composer require codepreneur/laravel-fathom
Publish the configuration (optional):
php artisan vendor:publish --tag=fathom-config
Fathom API Key
Generate an API key in your Fathom Settings → API Access.
Configuration
Add to your .env:
FATHOM_API_KEY=your-api-key FATHOM_WEBHOOK_SECRET=whsec_your-webhook-secret
Configuration options in config/fathom.php:
| Key | Description | Default |
|---|---|---|
api_key |
Your Fathom API key | — |
base_url |
API base URL | https://api.fathom.ai/external/v1 |
timeout |
HTTP timeout in seconds | 30 |
webhook.secret |
Webhook signing secret | — |
webhook.tolerance |
Signature timestamp tolerance (seconds) | 300 |
Basic Usage
use Codepreneur\Fathom\Facades\Fathom; $meetings = Fathom::meetings()->list(); $transcript = Fathom::recordings()->transcript($recordingId); $summary = Fathom::recordings()->summary($recordingId);
Meetings
$page = Fathom::meetings()->list(); foreach ($page->items as $meeting) { echo $meeting->title; echo $meeting->recordingId; echo $meeting->recordedBy->email; }
Meeting Filtering
$page = Fathom::meetings()->list([ 'recorded_by' => ['ceo@acme.com'], 'teams' => ['Sales', 'Engineering'], 'created_after' => '2025-01-01T00:00:00Z', 'created_before' => '2025-12-31T23:59:59Z', 'meeting_type' => 'Quarterly Business Review', 'calendar_invitees_domains_type' => 'one_or_more_external', 'include_transcript' => true, 'include_summary' => true, 'include_action_items' => true, 'include_highlights' => true, 'include_crm_matches' => true, ]);
Meeting Types
$types = Fathom::meetings()->types();
Pagination
Fathom uses cursor-based pagination:
$page = Fathom::meetings()->list(); foreach ($page->items as $meeting) { // process meeting } if ($page->hasMore()) { $nextPage = Fathom::meetings()->next($page); }
Transcripts
$transcript = Fathom::recordings()->transcript($recordingId); foreach ($transcript->entries as $entry) { echo $entry->speaker->displayName; echo $entry->speaker->matchedCalendarInviteeEmail; echo $entry->text; echo $entry->timestamp; // e.g. "00:05:32" }
For async delivery, pass a destination_url:
$transcript = Fathom::recordings()->transcript($recordingId, [ 'destination_url' => 'https://example.com/callback', ]);
Summaries
$summary = Fathom::recordings()->summary($recordingId); echo $summary->templateName; echo $summary->markdown;
Recording Downloads
$download = Fathom::recordings()->requestDownload($recordingId); while ($download->isProcessing()) { sleep(2); $download = Fathom::recordings()->downloadStatus($recordingId, $download->downloadId); } if ($download->isCompleted()) { $url = $download->video?->url ?? $download->audio?->url; }
Teams
$teams = Fathom::teams()->list(); foreach ($teams->items as $team) { echo $team->name; }
Team Members
$members = Fathom::teamMembers()->list(['team' => 'Sales']); foreach ($members->items as $member) { echo $member->name; echo $member->email; }
Users
Admin-only endpoint for listing users and permissions:
$users = Fathom::users()->list();
Webhooks
Create a Webhook
$webhook = Fathom::webhooks()->create([ 'destination_url' => 'https://example.com/webhooks/fathom', 'triggered_for' => ['my_recordings'], 'include_transcript' => true, 'include_summary' => true, 'include_action_items' => true, ]); // Store the secret securely — it is only returned once $webhook->id; $webhook->secret;
Available triggered_for values:
my_recordingsshared_external_recordingsmy_shared_with_team_recordings(Team Plans)shared_team_recordings(Team Plans)
Delete a Webhook
Fathom::webhooks()->delete($webhookId);
Webhook Verification
Fathom signs webhooks using HMAC SHA-256. Verify incoming requests:
if (Fathom::webhooks()->verify($request)) { $meeting = Fathom::webhooks()->meeting($request); }
Parse webhook payloads:
$payload = Fathom::webhooks()->payload($request); $meeting = Fathom::webhooks()->meeting($request);
Middleware
Register the middleware alias in your routes:
use App\Http\Controllers\FathomWebhookController; Route::post('/webhooks/fathom', FathomWebhookController::class) ->middleware('fathom.webhook');
Events
Listen for webhook events:
use Codepreneur\Fathom\Events\FathomMeetingReceived; use Codepreneur\Fathom\Events\FathomWebhookReceived; Event::listen(FathomMeetingReceived::class, StoreMeeting::class); Event::listen(FathomWebhookReceived::class, LogWebhook::class);
Dispatch events manually:
Fathom::webhooks()->dispatchEvents($request);
Exceptions
| Exception | HTTP Status |
|---|---|
AuthenticationException |
401, 403 |
NotFoundException |
404 |
ValidationException |
422 |
RateLimitException |
429 |
FathomException |
Other |
Handle rate limits:
use Codepreneur\Fathom\Exceptions\RateLimitException; try { Fathom::meetings()->list(); } catch (RateLimitException $e) { $e->limit(); // 60 $e->remaining(); // 0 $e->reset(); // seconds remaining in window $e->retryAfter(); // seconds to wait $e->resetAt(); // CarbonImmutable }
Rate Limits
Fathom enforces the following limits:
| Type | Limit |
|---|---|
| Global | 60 requests / 60 seconds |
| Heavy (transcript, summary, includes) | 30 requests / 60 seconds |
| Recording downloads | 30 requests / 60 seconds |
This package does not automatically retry rate-limited requests.
DTOs and Forward Compatibility
All responses are mapped to typed DTOs. Access unknown future API fields via raw():
$meeting = Fathom::meetings()->list()->items[0]; $meeting->title; // typed property $meeting->raw()['new_field']; // forward-compatible access
Testing
The package includes a full offline test suite using Http::fake():
composer test
composer analyse
composer lint
Contributing
See CONTRIBUTING.md.
Security
See SECURITY.md.
Changelog
See CHANGELOG.md.
License
The MIT License (MIT). See LICENSE for details.