liberusoftware/integrations-api
Laravel package that adds a REST API for managing integrations: register providers, connect and disconnect accounts, store credentials securely, and expose endpoints for listing, syncing and auditing integration data through configurable routes, with migrations, models and policies included.
This package is the HTTP/presentation layer for the liberusoftware/integrations domain module — it gives you routes, controllers, and response shaping for integrations without owning the business logic. Treat it as a thin adapter, not a framework.
Install and confirm what you actually got. With zero stars and no dependents, verify the surface area before writing code against it:
composer require liberusoftware/integrations-api
composer show liberusoftware/integrations-api # read the require block and autoload namespaces
php artisan vendor:publish --provider="Liberu\IntegrationsApi\IntegrationsApiServiceProvider"
php artisan route:list --path=api
The composer show output is your map. The require block tells you which domain package supplies models/contracts (likely liberusoftware/integrations or a shared liberusoftware/core), and the published config tells you the route prefix and middleware stack. If migrations are absent, they live in the domain module — run php artisan migrate after installing that too.
Look here first, in this order:
routes/ — the actual endpoint contract (verbs, paths, middleware).src/Http/Controllers and src/Http/Resources — how payloads are shaped and what relations are eager-loaded.config/integrations-api.php — prefix, middleware, pagination size, auth guard.src/Providers/*ServiceProvider.php — bindings and any contract-to-implementation maps you can override.First use case: authenticate a request and list integrations. Because the adapter is presentation-only, your fastest sanity check is a feature test, not a browser:
it('lists integrations for an authenticated user', function () {
$this->actingAs(User::factory()->create(), 'sanctum')
->getJson('/api/integrations')
->assertOk()
->assertJsonStructure(['data' => [['id', 'name', 'status']]]);
});
If that passes, the wiring (provider, routes, guard) is correct and you can start integrating. If it 404s, the provider isn't registered or the prefix differs; if it 401s, the guard in config doesn't match your auth setup.
Keep the adapter dumb and the domain smart. The correct division of labour: this package translates HTTP to domain calls and back. Validation rules, state transitions, and third-party API calls belong in the domain module or your own service classes. When you need behaviour the adapter doesn't expose, wrap it in your own controller that composes the domain service, rather than editing vendored code.
Override, don't fork. Presentation adapters in the Liberu ecosystem typically resolve resources/presenters from the container. Bind your own implementation in a AppServiceProvider:
public function register(): void
{
$this->app->bind(
\Liberu\IntegrationsApi\Http\Resources\IntegrationResource::class,
\App\Http\Resources\IntegrationResource::class
);
}
Then extend the shipped resource and add fields via toArray() — this survives composer update, which editing vendor/ does not.
Consistent response envelope. Decide once whether clients get data/meta/links (Laravel resource collections) or a flattened payload, and enforce it in a base resource. Mixed envelopes are the most common source of client-side breakage when other modules' APIs are consumed alongside this one.
Auth and scoping. Configure the guard and middleware in the published config, then scope every query to the authenticated tenant/user. The domain module usually exposes a query builder or repository; call it from a thin controller:
public function index(IntegrationRepository $integrations)
{
return IntegrationResource::collection(
$integrations->forTenant(auth()->user()->tenant_id)
->with(['credentials', 'latestSync'])
->paginate(config('integrations-api.per_page', 25))
);
}
Treat outbound integration calls as a separate concern. Presentation adapters typically do not queue or retry. If an endpoint triggers a sync against a third-party provider, dispatch a job from your controller and return 202 Accepted with a status resource, rather than blocking the request on a remote HTTP call.
Version your contract early. Even before public consumers exist, mount routes under a version segment (/api/v1/integrations) in config. Renaming resource fields later is far cheaper when you can ship v2 alongside.
Testing pattern. Use feature tests against the HTTP layer with RefreshDatabase and factories from the domain module. For outbound calls, Http::fake() and assert the request payload. Assert the JSON structure and key set, not the whole body — full-body assertions break on every harmless field addition.
License is NOASSERTION. This is the single biggest risk. Before shipping commercially, read the LICENSE file in the repository and confirm terms with the maintainer. NOASSERTION from a license detector means it could not be classified — not that it's permissive.
Zero stars, zero dependents, low opportunity score. Assume no community answers exist for your problems. Budget time to read the source: an adapter package is usually a few hundred lines across controllers, resources, and a provider, so an hour of reading saves days of guessing.
Release date is in the future (2026-10-26). Check composer show -a and the git tags before pinning. If the dated tag is unreachable or the repository only has dev-main, pin to a commit hash in composer.json so a force-push cannot silently change your dependency:
"liberusoftware/integrations-api": "dev-main#a1b2c3d"
Duplicate route registration. If both the package's service provider and the module's provider are active in bootstrap/providers.php (or modules_statuses.json), you can get duplicate route names and a route:cache failure. Run php artisan route:list --path=api/integrations and confirm each URI appears exactly once.
Config caching will bite you. Any env() call inside the package config file resolves to null once php artisan config:cache runs. Always read via config('integrations-api.key') in your own code, and re-run config:clear locally after editing the published config.
Serialization surprises. Watch for three recurring issues: dates emitted as 2026-10-26T00:00:00.000000Z versus a formatted string, enums serializing as their backing value (or failing to serialize at all), and monetary/credential fields leaking because $hidden wasn’t updated in the domain model. Add explicit casts/formatting in your resource and assert the exact shape in tests.
N+1 queries hide behind resources. A resource referencing $this->latestSync->status triggers a lazy load per row. Disable lazy loading in non-production (Model::preventLazyLoading()) and eager-load in the controller. This is the most common performance regression in adapter packages.
Paginate consistently. If the package's default per_page differs from other modules, clients must special-case it. Normalise through config and cap the maximum to prevent ?per_page=100000.
Error shape should match the success shape. Domain exceptions thrown from the module often surface as generic 500s. Register a handler mapping domain exceptions to problem-details JSON with the same envelope, otherwise frontends must parse two response formats.
Debugging path. php artisan route:list --path=api → Log::info() in a wrapping middleware → Telescope's request/response pair for the payload diff. For auth issues, php artisan route:list -v shows the resolved middleware stack and guard, which is usually where the mismatch is.
Extension points to look for before writing new code: container bindings for resources/repositories, a config-driven route prefix and middleware array, publishable config and (if present) views, and any Macroable response builders. If none exist, prefer composing the domain module in your own controller over patching the package.
How can I help you explore Laravel packages today?