---
title: "Custom delivery jobs — Campaigns for Laravel | Roundly"
description: "Implement ProcessesCampaignRecipient to deliver over SMS, push or any transport, with batch-safe cancellation and failure tracking."
url: https://roundly-consulting.com/open-source/docs/campaigns-for-laravel/custom-jobs
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[Campaigns for Laravel](https://roundly-consulting.com/open-source/docs/campaigns-for-laravel.md)

# Custom delivery jobs

To deliver over any other transport — an SMS gateway, a push service, a webhook — write your own job, implement Contracts\\ProcessesCampaignRecipient and register it:

```php
// config/campaigns.php
'process-recipient-job' => \App\Jobs\SendCampaignSms::class,
```

The contract fixes the constructor — (Campaign $campaign, CampaignRecipient $recipient) — and handle(CampaignManager $campaigns): void. The injected manager is the one behind the Campaigns facade, so record each outcome through the campaign’s handle:

```php
namespace App\Jobs;

use App\Services\SmsGateway; // your own SMS client
use Illuminate\Bus\Batchable;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use RoundlyConsulting\Campaigns\Campaign;
use RoundlyConsulting\Campaigns\CampaignRecipient;
use RoundlyConsulting\Campaigns\CampaignManager;
use RoundlyConsulting\Campaigns\Contracts\ProcessesCampaignRecipient;
use Throwable;

final class SendCampaignSms implements ProcessesCampaignRecipient, ShouldQueue
{
    use Batchable, Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function __construct(public Campaign $campaign, public CampaignRecipient $recipient) {}

    public function handle(CampaignManager $campaigns): void
    {
        if ($this->batch()?->cancelled()) {
            return; // the campaign was cancelled
        }

        try {
            app(SmsGateway::class)->send(
                $this->recipient->reachableAt,
                strip_tags($this->campaign->content),
            );

            $campaigns->campaign($this->campaign)->markProcessed($this->recipient);
        } catch (Throwable $e) {
            $campaigns->campaign($this->campaign)->markFailed($this->recipient, $e->getMessage());
        }
    }
}
```

## Checklist

- Use Illuminate\\Bus\\Batchable — start() adds your job to the campaign’s batch.
- Implement ShouldQueue so recipients are processed by queue workers.
- Return early when $this->batch()?->cancelled() is true, so a cancelled campaign stops sending.
- Record the outcome — catch exceptions and call ->markFailed($recipient, $message) on the handle, as the shipped jobs do — or let the job fail: a job that fails outright (throws, times out, runs out of attempts) counts as a failed delivery while the other recipients keep sending. Never both, or the recipient counts as failed twice.
- Call ->markProcessed($recipient) on success — it records the delivery and fires RecipientProcessed.
- Under Campaigns::fake() both calls are recorded, so assertRecipientProcessed() and assertRecipientFailed() test your job (see Testing).

## Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

[More ways to support, including crypto](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/campaigns-for-laravel.md)
