darvis / supplydrive-api
Laravel client for the Supplydrive API: send UBL invoices to Supplydrive, a Dutch PEPPOL access point, and fetch their delivery status by invoice number, message id or debtor, from PHP, artisan and an MCP server.
Requires
- php: ^8.2
- illuminate/cache: ^11.0|^12.0|^13.0
- illuminate/console: ^11.0|^12.0|^13.0
- illuminate/contracts: ^11.0|^12.0|^13.0
- illuminate/encryption: ^11.0|^12.0|^13.0
- illuminate/http: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
- illuminate/translation: ^11.0|^12.0|^13.0
- nesbot/carbon: ^2.72|^3.0
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/mcp: ^1.0
- laravel/pint: ^1.0
- orchestra/testbench: ^9.0|^10.0|^11.0
- pestphp/pest: ^3.0|^4.0
- pestphp/pest-plugin-laravel: ^3.0|^4.0
Suggests
- laravel/mcp: Required for the local MCP server that lets an AI agent look up message statuses (^1.0).
Provides
None
Conflicts
None
Replaces
None
README
A Laravel client for the Supplydrive API. Supplydrive is a Dutch PEPPOL access point and EDI provider: you send them an invoice, they deliver it. This package sends the invoice and asks their portal what happened to it: by your invoice number, by the message id they answered with, or per debtor.
It does not build the UBL document; darvis/ubl-peppol does that.
Features
- Sending -
SupplydriveSender::send($ublXml)posts the document and gives you the message id Supplydrive answered with, the key for its status history. Nothing is retried: a document sent twice is delivered twice - Login handled - the portal account is exchanged for an access token (OAuth password grant), kept encrypted in Laravel's cache until sixty seconds before it expires
- One new token after a 401 - when the portal refuses a token the client already had, it logs in again and repeats the call once
- Typed results - a
Messageper item withid,functionalId,statusandinterfaceId, and aMessageStatusenum for the eight statuses Supplydrive documents, withisDelivered(),isFailed()andisPending() - Descriptions in English and Dutch -
$message->description()in the locale of your application - Failures with a status - a
SupplydriveExceptionwith the HTTP status in$e->status(); a 404 is simply an empty list - Safe with your password - never in a URL, and replaced by
[redacted]in error messages, log lines and command output - Connection tests -
php artisan supplydrive:status --functional-id=F260001,php artisan supplydrive:send invoice.xml, andphp artisan supplydrive:health, which checks both endpoints without sending anything - Health checks in code -
healthCheck()on the client and the sender never throws and tells you reachable, account accepted, HTTP status and duration - An MCP server -
php artisan mcp:start supplydrivegives Claude and other AI agents thelookup-message-statusandexplain-statusestools, registered whenlaravel/mcpis installed - Laravel Boost - guideline and skill included, so an AI assistant in your app knows the API
Requirements
- PHP 8.2+
- Laravel 11, 12 or 13
- A Supplydrive portal account with the role for the status endpoint, and for sending the send URL and account; ask Supplydrive for them
laravel/mcp1.0 or higher for the MCP server (Laravel Boost installs it)
Installation
composer require darvis/supplydrive-api
SUPPLYDRIVE_USERNAME=your-username SUPPLYDRIVE_PASSWORD=your-password SUPPLYDRIVE_SEND_USERNAME=your-send-username SUPPLYDRIVE_SEND_PASSWORD=your-send-password
php artisan supplydrive:status --functional-id=F260001
The command prints ✓ Connection successful with a table of the messages, or ✗ Request failed: with the reason.
Quick start
use Darvis\SupplydriveApi\Exceptions\SupplydriveException; use Darvis\SupplydriveApi\SupplydriveClient; use Darvis\SupplydriveApi\SupplydriveSender; use Illuminate\Http\Client\ConnectionException; try { // Send: a 2xx gives the message id, a refusal throws, nothing is retried. $result = app(SupplydriveSender::class)->send($ublXml, $invoice->invoice_nr); $invoice->update(['peppol_sent_at' => now(), 'peppol_message_id' => $result->messageId]); // Look up: by invoice number, by the message id, or per debtor. $supplydrive = app(SupplydriveClient::class); foreach ($supplydrive->byFunctionalId($invoice->invoice_nr) as $message) { $message->status; // "Afgeleverd", as the API returns it $message->description(); // "The message has been delivered." $message->isDelivered(); // true } $supplydrive->byMessageId(1234); // the id Supplydrive answered with when you sent it $supplydrive->byDebtorKvk('12345678'); // the last hundred messages of that debtor $supplydrive->messageStatus(['debiteurBTW' => 'NL123456789B01', 'functioneelId' => 'F260001']); } catch (ConnectionException $e) { // Supplydrive could not be reached, or did not answer within the timeout. A send that timed // out may have arrived: look the invoice number up before you send it again. } catch (SupplydriveException $e) { $status = $e->status(); // 401, 403, 500 ..., or 0 when there was no response }
Documentation
The full documentation lives on the documentation site:
- Installation: the steps, the settings and how to check that it works
- Quick start: one complete example, a job that records the delivery on the invoice
- Sending: the send account, the message id, and what a timeout means
- How it works: the token, the repeat after a 401, the statuses, timeouts and where the password goes
- API reference: every public method, the enum, the exception and the commands
- Testing: fake Supplydrive in your tests
- MCP server: the handle, the two tools and how to add it to your agent
- Troubleshooting: every message with its cause and its fix
- FAQ: short answers
Laravel Boost
This package ships a guideline and a skill for Laravel Boost. Run php artisan boost:install, or php artisan boost:update --discover in a project that already uses Boost.
Testing
composer test # Pest composer lint # Pint, check only; composer format fixes composer analyse # Larastan
Changelog
See CHANGELOG.
Support the package
If darvis/supplydrive-api saves you time, buy me a beer 🍺: sponsor me on GitHub.
Contributing
See CONTRIBUTING.
Security
Please report a vulnerability privately, as described in SECURITY, not in the issue tracker.
License
The MIT License (MIT). See LICENSE.
This is an unofficial package, not made or endorsed by Supplydrive B.V.