Project Status:
This project is in an early sandbox phase. Right now, the focus is on defining scaffolding files, folder structure, and generation commands. It's not yet available as a Composer package. The examples you see here demonstrate the direction of travel and the developer experience we're aiming for.
Plenipotentiary
Structure, scaffolding, and sanity for API/SDK integrations in Laravel
A Laravel package helping developers structure API integrations so SDKs, REST APIs, SOAP services and MCP proxies (niche) all feel consistent, testable, and swappable. Provides Gateway/Adapter patterns and Artisan scaffolding. You write the integration code; the package adds validation, error handling, idempotency tracking, and a uniform Result interface across all your external services.
The Integration Challenge
Your Laravel app integrates with 3-5+ different types of services: Google Ads (official SDK), Mailchimp (REST), Stripe (official SDK), legacy SOAP, internal APIs. Each implemented differently.
Without a pattern, every integration is a special snowflake: different error handling, different return types, different testing strategies, different logging approaches. Whether you're a solo developer managing 8 integrations or a team of 10, this mix of different integration types creates maintenance chaos.
TL;DR
Think of Pleni like artisan:make
for third-party APIs: declare the provider, domain, context, and resource and instantly scaffold the DTOs, gateways, adapters and test harness you need. Plenipotentiary provides the contracts. You still implement the adapter logic (it's not magic), but the code now sits in a consistent, testable, tool-friendly structure.
Flysystem-style consistency for APIs, while recognizing not everything should be abstracted. Flysystem works because filesystems share common verbs. APIs don't: REST is philosophy, SDKs are vendor-specific, SOAP is legacy. Plenipotentiary gives them all the same architectural pattern, so your app doesn't care what's underneath.
plenipotentiary
a person GATEWAY/ADAPTER
, invested with the full power of independent action on behalf of their government DOMAIN
, typically in a foreign country API_PROVIDER
.
Quick Start Examples
Goal
A consistent way to work with diverse APIs.
Provide architectural patterns and tooling so that all integrations look consistent from the app's perspective, while allowing full access to underlying APIs.
This means:
- Consistency across SDK, REST, SOAP, and (optional) MCP proxy integrations
- Predictability in how your app interacts with ANY external service
- Testability - same mocking strategy for all integrations
- Discoverability - new dev sees Gateway pattern everywhere
- Swappability - change providers without touching business logic
- Focusability - AI code agents perform best within defined, repeatable patterns. Repetition turns AI output into reliability.
What This Is NOT
Even API creators struggle to maintain good SDKs. Third-party wrappers are doomed from the start. Plenipotentiary gives you patterns, not promises.
- Not an "API wrapper for X" - Wrappers promise complete coverage but deliver 20-40%. They can't keep up with API evolution.
- Not a "unified API client" - REST is a philosophy, not a protocol. Universal abstraction is impossible without losing unique provider value.
- Not complete endpoint coverage - It provides architecture and patterns. You implement the adapters you need.
- Not hiding complexity - Leaky abstractions break under real use. We provide structure, not magic.
- Not code generation for its own sake - Generated code is only valuable if it's maintainable and fits your architecture.
The Gateway Pattern: Your Architectural Anchor
Why This Pattern?
The Gateway/Adapter pattern provides a stable interception point between your application and external services. Once this structure is in place, you gain a single, consistent location to layer in production-grade features.
Without it, these concerns scatter across controllers, jobs, and service classes. Impossible to maintain consistently across diverse integrations (SDKs, REST, SOAP).
The pattern isn't magic; it's discipline. It gives you one place to solve each problem, once, for all integrations.
What You Can Layer In
Not every integration needs all of these. A simple lookup needs none. A financial integration needs them all.
AI agents thrive on patterns. Once you have real-world adapter examples, AI can generate additional adapters that follow your established conventions. With scaffolded tests already in place, it becomes a matter of briefly reviewing AI-generated code rather than writing from scratch; the patterns and test harness provide the guardrails.
Architecture
Promotes understanding vs over-abstraction - Five patterns, one stable platform
Core Principle
Abstract the Abstractable (CRUD): Pleni supports CRUD where it fits but doesn't pretend it covers everything. It offers multiple integration patterns to match different API shapes - each built on Laravel's native tooling and proven libraries like Saloon for HTTP.
How Plenipotentiary Works: The Flow
Your Application
Controllers, Jobs, Commands
Gateway
Stable, consistent contracts
Adapter
API integration logic
External API
Stripe, Google, etc.
What You Write
Your application code and the Adapter (actual API integration logic). This is NOT a magic wrapper; you still implement the integration, but with structure and safety guardrails.
What Plenipotentiary Provides
The Gateway layer (stable contracts, validation, policies) and scaffolding commands to generate boilerplate. Consistent interfaces enable robust test harnesses that work across all integration; Mock the Gateway, swap adapters for test doubles, verify behavior without external API calls.
Your Application Domain
Use any Laravel pattern you know - all return Result<T>
This is how your code will look. You still need to code the integration (adapter). Plenipotentiary offers a guided adapter approach that gives you structure, stability, and robustness without hiding the API from you.
Controllers
HTTP endpoints
Actions
Lorisleiva Actions
Jobs
Queue workers
Commands
Artisan CLI
Services
Business logic
AI Agents
LLM workflows
Consistent Result Interface Across All Patterns
Every pattern returns a consistent Result<T>
interface - whether Result<CanonicalDTO>
(CRUD) or Result<{UseCase}DTO>
(Operation). Predictable, testable, transport-agnostic. From simplest to most complex syntax:
$result = $gateway->create($dto);
if ($result->isOk()) {
// Success! Use the canonical DTO
$campaign = $result->unwrap();
echo $campaign->externalId; // '12345'
}
unwrap()
returns your domain DTO (CanonicalDTO for CRUD, {UseCase}DTO for Operation - consistent across providers).
rawResponse()
returns the actual provider response (Google's MutateCampaignsResponse, Stripe's Charge object, eBay's SearchResponse, etc.).
Use it for: Debugging, logging provider-specific metadata, accessing fields not in your domain DTO.
Consistent Interface Across All Laravel Patterns
Every pattern returns Result<T>
with the same methods:isOk()
,isErr()
,isInvalid()
,unwrap()
,rawResponse()
public function store(Request $req, CampaignGateway $gateway) {
$dto = CampaignCreateDTO::fromArray($req->validated());
$result = $gateway->create($dto);
return $result->isOk()
? response()->json($result->unwrap())
: response()->json($result->error(), 400);
}
public function store(Request $req, CreateCampaignAction $action) {
$result = $action->handle($req->validated());
return $result->isOk()
? response()->json($result->unwrap())
: response()->json($result->error(), 400);
}
// The Action internally uses the Gateway:
// class CreateCampaignAction {
// public function __construct(private CampaignGateway $gateway) {}
// public function handle(array $data): Result {
// $dto = CampaignCreateDTO::fromArray($data);
// return $this->gateway->create($dto);
// }
// }
Five Patterns for Different Integration Types
Different abstraction levels for different integration types. These patterns help you handle heterogeneous integrations (SDKs, REST, SOAP) with a consistent interface.
CRUD Pattern - Abstractable Resource Lifecycle
SDK or REST (Saloon)
Full lifecycle management with Create/Read/Update/Delete operations on resource-based APIs (campaigns, invoices, customers, products).
Use Cases:
Returns
Result<CanonicalDTO>
Repository
✓ Optional
Operation Pattern - Use Cases
SDK or REST (Saloon)
For operations beyond CRUD - search, generate, calculate, verify. Use this when the operation isn't about changing resource fields. If you need to pause a campaign (update status field), use CRUD + Laravel Actions instead. Avoids Gateway-calling-Gateway issues.
For operations that don't map to resource field changes
Use Cases:
Returns
Result<{UseCase}DTO>
Repository
✓ Optional/swappable
REST Pattern - Native Saloon
REST (Saloon)
Clean RESTful APIs using Saloon's native Request/Response pattern. Two modes: (1) Operation-like use cases use {UseCase}DTO with Gateway for validation/policies, (2) Simple calls use pure Saloon without Gateway overhead. For CRUD operations, use the CRUD pattern instead.
For CRUD operations, use CRUD pattern. REST is for operations and simple calls.
Use Cases:
Returns
Result<{UseCase}DTO> OR Saloon Response
Repository
✓ Flexible
Procedure Pattern - Rapid Prototyping
SDK or REST (Saloon)
Quick prototyping with dynamic operation names for fast iteration and exploration.
Use Cases:
Returns
Result<mixed>
Repository
✓ Optional/swappable
MCP Proxy Pattern - Controlled AI Agent Tool Access
HTTP API → MCP (stdio/SSE)
Proxy MCP servers through your Laravel app to add budget tracking, rate limiting, and audit trails when AI agents (Claude, ChatGPT) need controlled access to high-stakes tools (database, email, billing).
Proxies existing MCP servers, doesn't create new ones
Use Cases:
Returns
Result<McpToolResult>
Repository
✓ N/A (proxies existing MCP servers)
Gateway Layer: Your Stable Platform
The stable boundary that provides robustness
Gateway = Stable platform. Your application calls the Gateway, never the vendor API directly. When provider APIs change, only the Adapter changes - your Gateway stays stable. All gateways automatically apply cross-cutting concerns through Laravel's native tools.
Logging
Laravel Log facade
Retries
Automatic backoff
Idempotency
Laravel Cache
Error Mapping
Domain exceptions
Rate Limiting
Laravel RateLimiter
Observability
Metrics & events
Collaboration via INPUT_SPEC
All adapters define INPUT_SPEC
as their contract. When sharing adapters, INPUT_SPEC becomes an invaluable kickstart - self documenting errors ensures everyone knows exactly what fields are needed, validation rules, and defaults. This is what YOUR domain needs, not everything the API/SDK call supports (See step 4 in the developer workflow).
Understanding the MCP Proxy Pattern: Controlled AI Tool Access
This is a niche pattern for when AI agents (Claude, ChatGPT) need access to high-stakes tools (database queries, email sending, billing operations) and you need budget tracking, rate limiting, and complete audit trails. Your Laravel app acts as a controlled proxy between the AI agent and existing MCP servers.
Adapter Layer
Your actual API integration code
Gateway delegates to Adapters - Adapters contain your actual API integration code, implemented according to the pattern you selected (CRUD, Operation, REST, Procedure, or MCP Proxy). Each adapter translates between your domain (DTOs, INPUT_SPEC) and the provider's API (SDK calls, HTTP requests, SOAP).
When provider APIs change, you update the Adapter implementation - the Gateway contract and your application code stay stable.
External API
Third-party services
Adapters call External APIs - The third-party services your application integrates with: Stripe, Google Ads, Mailchimp, OpenAI, eBay, or any custom API. Your application never calls these directly - always through the Gateway → Adapter layer.
This indirection provides stability, testability, and consistent error handling across all your integrations.