PHPackages                             callmelater/laravel - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Queues &amp; Workers](/categories/queues)
4. /
5. callmelater/laravel

ActiveLibrary[Queues &amp; Workers](/categories/queues)

callmelater/laravel
===================

Laravel SDK for CallMeLater - Schedule HTTP calls and interactive reminders

v0.3.1(2mo ago)04MITPHPPHP ^8.2

Since Mar 1Pushed 2mo agoCompare

[ Source](https://github.com/Canell/callmelater-laravel)[ Packagist](https://packagist.org/packages/callmelater/laravel)[ Docs](https://callmelater.io)[ RSS](/packages/callmelater-laravel/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (37)Versions (5)Used By (0)

CallMeLater Laravel SDK
=======================

[](#callmelater-laravel-sdk)

A fluent Laravel SDK for [CallMeLater](https://callmelater.io) - schedule durable HTTP calls and interactive reminders.

Requirements
------------

[](#requirements)

- PHP 8.2+
- Laravel 10, 11, 12, or 13

Installation
------------

[](#installation)

```
composer require callmelater/laravel
```

Configuration
-------------

[](#configuration)

Add your API credentials to `.env`:

```
CALLMELATER_API_TOKEN=sk_live_your_token_here
CALLMELATER_WEBHOOK_SECRET=your_webhook_secret
```

Optionally publish the config file:

```
php artisan vendor:publish --tag=callmelater-config
```

Usage
-----

[](#usage)

### Scheduling HTTP Calls

[](#scheduling-http-calls)

```
use CallMeLater\Laravel\Facades\CallMeLater;

// Simple scheduled request
CallMeLater::http('https://api.example.com/process')
    ->post()
    ->payload(['user_id' => 123])
    ->inHours(2)
    ->send();

// With full options
CallMeLater::http('https://api.example.com/webhook')
    ->name('Process order #456')
    ->method('POST')
    ->headers(['X-Custom-Header' => 'value'])
    ->payload(['order_id' => 456, 'action' => 'ship'])
    ->at(now()->addDays(3))
    ->timezone('America/New_York')
    ->retry(5, 'exponential', 120)
    ->callback('https://myapp.com/callbacks/callmelater')
    ->metadata(['source' => 'laravel-app'])
    ->send();

// Using presets
CallMeLater::http('https://api.example.com/reminder')
    ->post()
    ->at('tomorrow')  // or 'next_monday', 'end_of_week', etc.
    ->send();
```

### Sending Reminders

[](#sending-reminders)

```
use CallMeLater\Laravel\Facades\CallMeLater;

// Simple reminder
CallMeLater::reminder('Approve deployment')
    ->to('manager@example.com')
    ->message('Please approve the production deployment for v2.0')
    ->at('tomorrow 9am')
    ->send();

// With all options
CallMeLater::reminder('Weekly report sign-off')
    ->to('cfo@example.com')
    ->toMany(['ceo@example.com', 'coo@example.com'])
    ->message('Please review and approve the weekly financial report')
    ->buttons('Approve', 'Reject')
    ->allowSnooze(3)
    ->requireAll()  // All recipients must respond
    ->expiresInDays(7)
    ->escalateTo(['backup-approver@example.com'], afterHours: 48)
    ->attach('https://example.com/reports/weekly.pdf', 'Weekly Report.pdf')
    ->callback('https://myapp.com/callbacks/reminder-response')
    ->metadata(['report_id' => 'WR-2024-01'])
    ->inDays(1)
    ->send();
```

### Recurring Actions

[](#recurring-actions)

Make any action repeat on a schedule:

```
use CallMeLater\Laravel\Facades\CallMeLater;

// Repeat every 2 hours, up to 10 times
CallMeLater::http('https://api.example.com/reports/generate')
    ->post()
    ->payload(['type' => 'health_check'])
    ->inMinutes(5)
    ->everyHours(2)
    ->maxOccurrences(10)
    ->send();

// Repeat every day forever
CallMeLater::http('https://api.example.com/cleanup')
    ->post()
    ->inHours(1)
    ->everyDays(1)
    ->repeatForever()
    ->send();

// Repeat weekly until a specific date
CallMeLater::http('https://api.example.com/reports/weekly')
    ->post()
    ->at('next_monday')
    ->everyWeeks(1)
    ->until('2026-12-31T23:59:59Z')
    ->send();

// Recurring reminders
CallMeLater::reminder('Weekly standup check-in')
    ->to('team@example.com')
    ->message('Please confirm your standup attendance')
    ->at('next_monday')
    ->everyWeeks(1)
    ->maxOccurrences(52)
    ->send();
```

Recurring actions work with both HTTP actions and reminders. The minimum interval is 5 minutes.

### Chains (Multi-Step Workflows)

[](#chains-multi-step-workflows)

```
use CallMeLater\Laravel\Facades\CallMeLater;

// Build a multi-step workflow
CallMeLater::chain('Process Order')
    ->input(['order_id' => 456])
    ->addHttpStep('Charge Payment')
        ->url('https://api.stripe.com/v1/charges')
        ->post()
        ->body(['amount' => 2999])
        ->maxAttempts(3)
        ->done()
    ->addGateStep('Approve Shipping')
        ->message('Approve shipment for order #456?')
        ->to('warehouse@example.com')
        ->timeout('2d')
        ->onTimeout('cancel')
        ->done()
    ->addDelayStep('Wait 1 hour')
        ->hours(1)
        ->done()
    ->addHttpStep('Ship Order')
        ->url('https://shipping.example.com/ship')
        ->post()
        ->body(['order_id' => '{{input.order_id}}'])
        ->condition("{{steps.1.response.action}} == confirmed")
        ->done()
    ->errorHandling('fail_chain')
    ->send();

// Get a chain
$chain = CallMeLater::getChain('chn_123');

// List chains with filters
$chains = CallMeLater::listChains(['status' => 'running']);

// Cancel a running chain
CallMeLater::cancelChain('chn_123');
```

### Templates (Reusable Action Definitions)

[](#templates-reusable-action-definitions)

```
use CallMeLater\Laravel\Facades\CallMeLater;

// Create a template
$tpl = CallMeLater::template('Invoice Reminder')
    ->description('Sends reminder to approve an invoice')
    ->mode('gated')
    ->gateConfig([
        'message' => 'Please approve invoice #{{invoice_id}}',
        'recipients' => ['email:{{approver_email}}'],
    ])
    ->placeholder('invoice_id', required: true, description: 'The invoice number')
    ->placeholder('approver_email', required: true)
    ->send();

// Trigger a template with placeholder values
CallMeLater::trigger($tpl['trigger_token'], [
    'invoice_id' => 'INV-001',
    'approver_email' => 'boss@example.com',
]);

// Update a template
CallMeLater::template('Updated Name')
    ->description('New description')
    ->update('tpl_123');

// CRUD operations
$template = CallMeLater::getTemplate('tpl_123');
$templates = CallMeLater::listTemplates();
CallMeLater::deleteTemplate('tpl_123');
CallMeLater::toggleTemplate('tpl_123');
CallMeLater::regenerateTemplateToken('tpl_123');
$limits = CallMeLater::templateLimits();
```

### Managing Actions

[](#managing-actions)

```
// Get an action
$action = CallMeLater::get('action_id_here');

// List actions with filters
$actions = CallMeLater::list([
    'status' => 'resolved',
    'type' => 'webhook',       // 'webhook' for HTTP calls, 'approval' for reminders
    'per_page' => 50,
]);

// Cancel an action
CallMeLater::cancel('action_id_here');
```

### Handling Webhooks

[](#handling-webhooks)

The SDK provides a built-in webhook handler that verifies signatures and dispatches Laravel events automatically:

```
// routes/web.php
use CallMeLater\Laravel\Facades\CallMeLater;

Route::post('/webhooks/callmelater', function (Illuminate\Http\Request $request) {
    CallMeLater::webhooks()->handle($request);
    return response()->json(['received' => true]);
});
```

This verifies the `X-CallMeLater-Signature` header and dispatches the appropriate Laravel event (`ActionExecuted`, `ActionFailed`, `ActionExpired`, or `ReminderResponded`).

You can also skip signature verification (useful in local development) or disable event dispatching:

```
// Skip signature verification
CallMeLater::webhooks()->skipVerification()->handle($request);

// Handle without dispatching events (returns the parsed payload)
$payload = CallMeLater::webhooks()->withoutEvents()->handle($request);
```

Alternatively, use the middleware for route-level signature verification:

```
use CallMeLater\Laravel\Http\Middleware\VerifyCallMeLaterSignature;

Route::post('/webhooks/callmelater', [WebhookController::class, 'handle'])
    ->middleware(VerifyCallMeLaterSignature::class);
```

### Listening to Events

[](#listening-to-events)

```
// app/Providers/EventServiceProvider.php
protected $listen = [
    \CallMeLater\Laravel\Events\ReminderResponded::class => [
        \App\Listeners\HandleReminderResponse::class,
    ],
];
```

```
// app/Listeners/HandleReminderResponse.php
namespace App\Listeners;

use CallMeLater\Laravel\Events\ReminderResponded;

class HandleReminderResponse
{
    public function handle(ReminderResponded $event): void
    {
        if ($event->isConfirmed()) {
            // Handle confirmation
            logger()->info("Reminder {$event->actionName} was confirmed by {$event->responderEmail}");
        } elseif ($event->isDeclined()) {
            // Handle decline
            logger()->warning("Reminder {$event->actionName} was declined");
        }
    }
}
```

Artisan Commands
----------------

[](#artisan-commands)

```
# List your scheduled actions
php artisan callmelater:list
php artisan callmelater:list --status=resolved --type=webhook

# Cancel an action
php artisan callmelater:cancel action_id_here
```

Dependency Injection
--------------------

[](#dependency-injection)

You can also use dependency injection instead of the facade:

```
use CallMeLater\Laravel\CallMeLater;

class OrderController extends Controller
{
    public function __construct(
        private CallMeLater $callMeLater
    ) {}

    public function scheduleFollowUp(Order $order)
    {
        $this->callMeLater->reminder("Follow up on order #{$order->id}")
            ->to($order->customer_email)
            ->message("How was your experience with your recent order?")
            ->inDays(7)
            ->send();
    }
}
```

Debugging
---------

[](#debugging)

Both builders provide methods to inspect the API payload before sending:

```
// Inspect the payload as an array
$payload = CallMeLater::http('https://api.example.com/process')
    ->post()
    ->payload(['user_id' => 123])
    ->inHours(2)
    ->toArray();

// Dump and die (useful during development)
CallMeLater::reminder('Test')
    ->to('user@example.com')
    ->message('Debug this')
    ->inHours(1)
    ->dd();
```

Error Handling
--------------

[](#error-handling)

The SDK throws specific exceptions for different error types:

```
CallMeLaterException (base)
├── ConfigurationException     — Missing API token or webhook secret
└── ApiException               — API returned an error response
    └── AuthenticationException — 401 Unauthorized

SignatureVerificationException — Invalid webhook signature (extends InvalidArgumentException)

```

### Catching API Errors

[](#catching-api-errors)

```
use CallMeLater\Laravel\Exceptions\ApiException;
use CallMeLater\Laravel\Exceptions\AuthenticationException;

try {
    CallMeLater::http('https://example.com')->send();
} catch (AuthenticationException $e) {
    // Invalid or expired API token
    logger()->error('Auth failed: ' . $e->getMessage());
} catch (ApiException $e) {
    // Access the HTTP status code
    $e->getStatusCode();       // 422, 404, 500, etc.

    // Access the full validation error bag (for 422 responses)
    $e->getValidationErrors(); // ['mode' => ['The selected mode is invalid.'], ...]
    $e->getErrorBag();         // Alias for getValidationErrors()

    // Access the raw response body
    $e->getResponseBody();
}
```

API Reference
-------------

[](#api-reference)

### HTTP Action Builder

[](#http-action-builder)

MethodDescription`method(string $method)`Set HTTP method (GET, POST, PUT, PATCH, DELETE)`get()`, `post()`, `put()`, `patch()`, `delete()`Shortcut methods`headers(array $headers)`Set request headers`header(string $key, string $value)`Add a single header`payload(mixed $data)`Set request body`name(string $name)`Set a friendly name`at(DateTime|string $time)`Schedule at specific time or preset`delay(int $amount, string $unit)`Schedule after delay`inMinutes(int)`, `inHours(int)`, `inDays(int)`Delay shortcuts`timezone(string $tz)`Set timezone`retry(int $max, string $backoff, int $delay)`Configure retry policy`noRetry()`Disable retries`callback(string $url)`Set callback URL`metadata(array $data)`Set metadata`meta(string $key, mixed $value)`Add a single metadata entry`idempotencyKey(string $key)`Set idempotency key`repeat(int $freq, string $unit)`Enable recurrence (e.g., `repeat(2, 'hours')`)`every(int $freq, string $unit)`Alias for `repeat()``everyMinutes(int)`, `everyHours(int)`, `everyDays(int)`, `everyWeeks(int)`, `everyMonths(int)`Recurrence shortcuts`maxOccurrences(int $count)`Stop after N executions`until(DateTime|string $date)`Stop after a date`repeatForever()`No end condition (default)`toArray()`Get the API payload without sending`dd()`Dump the payload and die`send()`Send the action### Reminder Builder

[](#reminder-builder)

MethodDescription`to(string $email)`Add email recipient`toMany(array $emails)`Add multiple recipients`toPhone(string $phone)`Add SMS recipient`toChannel(string $uuid)`Add chat channel recipient`message(string $text)`Set reminder message`buttons(string $confirm, string $decline)`Customize button text`allowSnooze(int $max)`Allow snoozing`noSnooze()`Disable snoozing`expiresInDays(int $days)`Set token expiry`requireAll()`Require all recipients to respond`firstResponse()`Complete on first response`escalateTo(array $contacts, int $hours)`Add escalation`attach(string $url, ?string $name)`Add attachment`callback(string $url)`Set callback URL`metadata(array $data)`Add metadata`toRecipient(string $selector)`Add a raw recipient selector URI`repeat(int $freq, string $unit)`Enable recurrence (e.g., `repeat(1, 'weeks')`)`every(int $freq, string $unit)`Alias for `repeat()``everyMinutes(int)`, `everyHours(int)`, `everyDays(int)`, `everyWeeks(int)`, `everyMonths(int)`Recurrence shortcuts`maxOccurrences(int $count)`Stop after N executions`until(DateTime|string $date)`Stop after a date`repeatForever()`No end condition (default)`toArray()`Get the API payload without sending`dd()`Dump the payload and die`send()`Send the reminder### Chain Builder

[](#chain-builder)

MethodDescription`input(array $data)`Set chain input data (available as `{{input.*}}` in steps)`addHttpStep(string $name)`Add an HTTP step, returns `HttpStepBuilder``addGateStep(string $name)`Add a gate (approval) step, returns `GateStepBuilder``addDelayStep(string $name)`Add a delay step, returns `DelayStepBuilder``errorHandling(string $strategy)`Set error handling (`fail_chain`, `skip_step`, `continue`)`toArray()`Get the API payload without sending`dd()`Dump the payload and die`send()`Send the chain### HTTP Step Builder (within chains)

[](#http-step-builder-within-chains)

MethodDescription`url(string $url)`Set request URL`method(string $method)`Set HTTP method`get()`, `post()`, `put()`, `patch()`, `delete()`Method shortcuts`headers(array $headers)`Set request headers`body(mixed $data)`Set request body`maxAttempts(int $max)`Set retry attempts`retryStrategy(string $strategy)`Set retry strategy`condition(string $expr)`Set condition expression`done()` / `add()`Return to chain builder### Gate Step Builder (within chains)

[](#gate-step-builder-within-chains)

MethodDescription`message(string $text)`Set gate message`to(string $email)`Add email recipient`toMany(array $emails)`Add multiple recipients`toRecipient(string $uri)`Add raw recipient URI`maxSnoozes(int $max)`Set max snoozes`requireAll()` / `firstResponse()`Set confirmation mode`timeout(string $duration)`Set timeout (e.g., `'2d'`, `'24h'`)`onTimeout(string $action)`Set timeout action (`cancel`, `continue`, `fail`)`condition(string $expr)`Set condition expression`done()` / `add()`Return to chain builder### Delay Step Builder (within chains)

[](#delay-step-builder-within-chains)

MethodDescription`duration(string $dur)`Set raw duration (e.g., `'4h30m'`)`minutes(int)`, `hours(int)`, `days(int)`Duration shortcuts`condition(string $expr)`Set condition expression`done()` / `add()`Return to chain builder### Template Builder

[](#template-builder)

MethodDescription`description(string $text)`Set template description`type(string $type)`Set action type (`http`, `reminder`, `chain`)`mode(string $mode)`Set mode (`immediate`, `gated`)`timezone(string $tz)`Set default timezone`requestConfig(array $config)`Set HTTP request config`gateConfig(array $config)`Set gate config`maxAttempts(int $max)`Set retry attempts`retryStrategy(string $strategy)`Set retry strategy`placeholder(string $key, ...)`Add a placeholder (with `required`, `description`, `default`)`placeholders(array $list)`Set all placeholders at once`chainSteps(array $steps)`Set chain steps (for chain templates)`chainErrorHandling(string $strategy)`Set chain error handling`coordinationKeys(array $keys)`Set coordination keys`coordinationConfig(array $config)`Set coordination config`toArray()`Get the API payload without sending`dd()`Dump the payload and die`send()` / `create()`Create the template`update(string $id)`Update an existing template### Chain &amp; Template Management

[](#chain--template-management)

MethodDescription`CallMeLater::chain($name)`Create a chain builder`CallMeLater::getChain($id)`Get a chain by ID`CallMeLater::listChains($filters)`List chains`CallMeLater::cancelChain($id)`Cancel a running chain`CallMeLater::template($name)`Create a template builder`CallMeLater::getTemplate($id)`Get a template by ID`CallMeLater::listTemplates($filters)`List templates`CallMeLater::deleteTemplate($id)`Delete a template`CallMeLater::toggleTemplate($id)`Toggle enabled/disabled`CallMeLater::regenerateTemplateToken($id)`Regenerate trigger token`CallMeLater::templateLimits()`Get account template limits`CallMeLater::trigger($token, $params)`Trigger a template### Signature Verification

[](#signature-verification)

MethodDescription`CallMeLater::verifySignature($request)`Verify signature or throw `SignatureVerificationException``CallMeLater::isValidSignature($request)`Returns `true`/`false` without throwingTesting
-------

[](#testing)

Run the SDK test suite:

```
cd packages/laravel-callmelater
composer install
./vendor/bin/phpunit
```

License
-------

[](#license)

MIT

###  Health Score

36

—

LowBetter than 82% of packages

Maintenance88

Actively maintained with recent releases

Popularity5

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity39

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~9 days

Total

4

Last Release

61d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/b787c401618374bd52f68d40cca36ac5858c567ad3c54478e67b84610c472003?d=identicon)[fbarse](/maintainers/fbarse)

---

Top Contributors

[![flavienb](https://avatars.githubusercontent.com/u/26281844?v=4)](https://github.com/flavienb "flavienb (7 commits)")

---

Tags

schedulerlaravelqueuecronwebhooksdeferredreminderscallmelater

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/callmelater-laravel/health.svg)

```
[![Health](https://phpackages.com/badges/callmelater-laravel/health.svg)](https://phpackages.com/packages/callmelater-laravel)
```

###  Alternatives

[bagisto/bagisto

Bagisto Laravel E-Commerce

26.2k161.6k7](/packages/bagisto-bagisto)[unopim/unopim

UnoPim Laravel PIM

9.4k1.8k](/packages/unopim-unopim)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
