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

Pivotal Tracker Laravel Package

laravel-notification-channels/pivotal-tracker

A Laravel notification channel for Pivotal Tracker. Add it to your notification classes to push updates from your app into Pivotal Tracker, keeping project tracking in sync with events in your Laravel application.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is a single-purpose Laravel notification channel: it lets a notification class deliver a story into a Pivotal Tracker project via a toPivotalTracker() method, using config from config/services.php. It was released once, in August 2016, as 0.0.1, and the repository is archived. The README-level claims about "Laravel 8+" and "Pivotal Tracker API v5" should be verified against composer.json before you believe them — a 2016 tag predates auto-discovery (Laravel 5.5) and most of the API surface the description implies.

Minimal steps

  1. Confirm it installs at all. On a scratch branch, pin the exact tag:

    composer require laravel-notification-channels/pivotal-tracker:0.0.1
    

    Read the resulting composer.json constraints (illuminate/support, php) and run composer why-not laravel/framework if Composer refuses. If the constraints cap out below your Laravel version, stop here — the rest is a fork-and-patch exercise.

  2. Register the provider manually. A 2016 package almost certainly has no extra.laravel.providers block. Add it to config/app.php (providers array) on Laravel 10 and below, or bootstrap/providers.php on 11+.

  3. Configure credentials. The channel reads a token and a project ID; the convention for this org is a services.php block:

    // config/services.php
    'pivotal_tracker' => [
        'token'      => env('PIVOTAL_TRACKER_TOKEN'),
        'project_id' => env('PIVOTAL_TRACKER_PROJECT_ID'),
    ],
    

    Open src/ to confirm the actual key names — PivotalTrackerChannel and PivotalTrackerMessage are the two files that matter, and the channel's constructor/config lookups are the ground truth, not the description.

  4. Check the endpoint. Grep for pivotaltracker.com in src/. If it targets services/v3 or v4, the hardcoded URL will 404 against the current API; that's a one-line edit, but you need to know before debugging a silent failure.

Where to look first, in order: composer.json → the channel class (send()) → the message class (builder methods and which fields it actually serializes) → config/. There are no tests in a 0.0.1 archived package, so the channel's send() is your documentation.

First use case: an internal "ops" notification — new enterprise signup, failed deploy, exception threshold breached — that creates a story in your team's tracker project. Point it at a scratch project first; the failure mode of a misconfigured tracker channel is a pile of junk stories in a project your PM can see.

Implementation Patterns

The notification class is the whole integration. Day-to-day, you never touch the package directly — you write a notification and let via() name the channel:

use Illuminate\Notifications\Notification;
use NotificationChannels\PivotalTracker\PivotalTrackerChannel;
use NotificationChannels\PivotalTracker\PivotalTrackerMessage;

class DeployFailed extends Notification
{
    public function __construct(public array $context) {}

    public function via($notifiable): array
    {
        return [PivotalTrackerChannel::class];
    }

    public function toPivotalTracker($notifiable): PivotalTrackerMessage
    {
        return PivotalTrackerMessage::create()
            ->name('Deploy failed: '.$this->context['env'])
            ->description($this->context['log_excerpt'])
            ->type('bug')
            ->labels(['ops', $this->context['env']]);
    }
}

Routing belongs on the notifiable, unless you use on-demand. For a fixed project, the global project_id in services.php is enough. For per-team projects, implement routeNotificationForPivotalTracker() on the notifiable model (returning a project ID) and have the channel prefer it over config. For one-offs — a cron job, a queued listener with no model — use on-demand routing instead of inventing a fake user:

Notification::route('pivotal_tracker', config('services.pivotal_tracker.project_id'))
    ->notify(new DeployFailed($context));

Always implement ShouldQueue. The channel makes a synchronous HTTP call inside send(). If the notification is dispatched from a request or a model observer, that's an outbound API call on your response path, and a Tracker outage becomes your outage. implements ShouldQueue moves it to the queue, where a failed job is retried by your normal worker policy.

Treat the tracker as a secondary channel. Notifications support multiple channels; keep mail/Slack as the primary and add Pivotal Tracker alongside, not instead of:

public function via($notifiable): array
{
    return config('features.tracker_stories')
        ? ['mail', PivotalTrackerChannel::class]
        : ['mail'];
}

That flag is your kill switch when the token expires or the API changes, without a deploy.

Testing follows the standard notification idiom. Use Notification::fake() and assert on the notification's payload rather than on HTTP:

Notification::fake();
$this->post('/deploy', [...]);
Notification::assertSentOnDemand(DeployFailed::class, function ($n, $channels, $notifiable) {
    return $n->context['env'] === 'production';
});

Because toPivotalTracker() is pure — it just builds a message object — you can also unit-test it directly with no fake and no network, which is the cheapest possible coverage for a package with none of its own.

Deduplicate at your layer, not the package's. The channel will happily create a story every time it's called. For recurring alerts, keep a short-lived cache key (Cache::add("tracker:{$fingerprint}", true, now()->addHour())) and bail before notifying. Story spam is how these integrations get switched off by the team that owns the board.

Isolate the import. Wrap the channel class name behind a constant or a small OpsNotifier service so a future replacement — including writing the ~60-line channel yourself — is a single-file edit. Given the archival status, this isn't paranoia; it's the expected path.

Gotchas and Tips

The package is archived and unreleased since 2016. No security fixes, no Laravel compatibility work, no support. Before investing, budget an hour to read src/ end to end — if it's under ~150 lines (likely), the honest calculation is that forking it into app/Notifications/Channels/PivotalTrackerChannel.php and deleting the dependency is cheaper than owning a 0.0.1 Composer constraint. A hand-rolled channel needs only a send() method, a Http::withToken(...)->post(...), and your own message DTO.

Verify Pivotal Tracker itself is still available to you. The product has had a long and public end-of-life conversation. Confirm the workspace and API token still work (a curl against /services/v5/projects/{id} is enough) before you debug a single line of PHP.

Manual provider registration, and cached config hiding it. There is no auto-discovery in this era. After editing config/app.php or bootstrap/providers.php, run php artisan optimize:clear — a cached config file will keep serving the old provider list and the class will look "not found" for reasons that have nothing to do with the package.

Config cache and env vars. config:cache freezes services.pivotal_tracker, and env() calls outside config files return null in cached mode. Always read credentials through config(), never env(), in the channel or your wrapper — and re-run php artisan config:cache in deploy scripts after changing the token.

The API token is a secret that can create and modify stories. Keep it in .env, never in services.php defaults, and scope it to a single project if your tracker plan allows. Rotate it when someone leaves; a leaked token lets anyone file or edit stories in your board.

A thrown exception in send() takes the whole notification down. Laravel resolves channels in sequence; if the tracker returns 401 or times out, the mail channel in the same via() array may never run depending on ordering and your exception handler. Either put the tracker channel last, or catch and log inside your own channel wrapper rather than relying on the package's error handling, which likely doesn't exist.

Message field names don't map 1:1 to API v5. The builder methods (name, description, type, labels) correspond to story attributes, but type accepts a narrow enum (feature, bug, chore, release) and an invalid value returns a 400 with a body the channel may swallow. Log the response body on non-2xx during bring-up; a silent no-op story is the classic first-day bug.

No rate-limit handling. Tracker's API will throttle you. If you're filing stories from an alerting path, batch or debounce upstream — the package won't retry with backoff, and your queue's retry policy may just hammer the endpoint.

Extension points are the same as any notification channel. The notification class itself is the seam: subclass or replace it, not the channel. If you need conditional story routing, routeNotificationForPivotalTracker() on the notifiable is the supported hook and requires no package modification.

Revisit at the point of need, not the point of upgrade. Since there is no upgrade path, decide now: fork-and-own, or reimplement. Either outcome is a smaller commitment than tracking an archived 0.0.1 tag, and the notification API you write against — via(), toX(), routeNotificationForX() — is stable Laravel core regardless of what happens to this package.

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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
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