FleetbaseFleetbase

Adding an Integrated Vendor

Wire a third-party logistics provider into Fleet-Ops as a selectable IntegratedVendor — credentials form, API bridge, lifecycle callbacks, and webhook routing.

Adding an Integrated Vendor

Fleet-Ops's IntegratedVendor system lets users connect external delivery providers (Lalamove, etc.) and dispatch orders to them as if they were internal vendors. The integration consists of:

  1. A bridge class — wraps the third-party API
  2. Optional service-type and country bridges — for selectable services and regional routing
  3. A descriptor entry registered with the platform — credentials schema, callbacks, branding

Out of the box, Fleet-Ops ships Lalamove. New vendors plug in by registering a descriptor that points at your bridge class.

1. Build the Bridge

The bridge is an injectable PHP class that talks to the third-party API. It must accept its credentials via constructor injection — that's how the platform supplies user-entered API keys at runtime.

<?php
// server/src/Integrations/AcmeDispatch/AcmeDispatch.php

namespace MyOrg\MyExtension\Integrations\AcmeDispatch;

use Fleetbase\FleetOps\Models\IntegratedVendor;
use Illuminate\Support\Facades\Http;

class AcmeDispatch
{
    protected ?IntegratedVendor $vendor = null;

    public function __construct(
        protected string $apiKey,
        protected string $apiSecret,
        protected bool $sandbox = false,
        protected ?string $market = null,
    ) {}

    public function setIntegratedVendor(IntegratedVendor $vendor): static
    {
        $this->vendor = $vendor;
        return $this;
    }

    protected function host(): string
    {
        return $this->sandbox
            ? 'https://sandbox.acme-dispatch.com/'
            : 'https://api.acme-dispatch.com/';
    }

    public function quote(array $params): array
    {
        return Http::withBasicAuth($this->apiKey, $this->apiSecret)
            ->baseUrl($this->host())
            ->post('v1/quotes', $params)
            ->throw()
            ->json();
    }

    public function createDelivery(array $params): array
    {
        return Http::withBasicAuth($this->apiKey, $this->apiSecret)
            ->baseUrl($this->host())
            ->post('v1/deliveries', $params)
            ->throw()
            ->json();
    }

    /** Lifecycle callback wired in the descriptor below. */
    public function setWebhook(string $webhookUrl): void
    {
        Http::withBasicAuth($this->apiKey, $this->apiSecret)
            ->baseUrl($this->host())
            ->post('v1/webhooks', ['url' => $webhookUrl]);
    }

    public function cancelFromFleetbaseOrder(): void
    {
        // Cancel any in-flight delivery the vendor is tracking
    }
}

2. Register the Descriptor

In your service provider's boot(), append a descriptor to IntegratedVendors::$supported:

<?php
// server/src/Providers/MyExtensionServiceProvider.php

use Fleetbase\FleetOps\Support\IntegratedVendors;
use MyOrg\MyExtension\Integrations\AcmeDispatch\AcmeDispatch;

public function boot()
{
    parent::boot();

    IntegratedVendors::$supported[] = [
        'name'             => 'Acme Dispatch',
        'code'             => 'acme-dispatch',
        'host'             => 'https://api.acme-dispatch.com/',
        'sandbox'          => 'https://sandbox.acme-dispatch.com/',
        'namespace'        => 'v1',
        'bridge'           => AcmeDispatch::class,
        // Optional — list of credential fields the user fills out
        'credentialParams' => [
            ['key' => 'api_key'],
            ['key' => 'api_secret'],
        ],
        // Optional — additional config the user sets
        'optionParams' => [
            ['key' => 'market', 'options' => [
                ['code' => 'us', 'key' => 'United States'],
                ['code' => 'mx', 'key' => 'Mexico'],
            ], 'optionValue' => 'code', 'optionLabel' => 'key'],
        ],
        // Maps user-saved values onto your bridge's constructor args
        'bridgeParams' => [
            'apiKey'    => 'credentials.api_key',
            'apiSecret' => 'credentials.api_secret',
            'sandbox'   => 'sandbox',
            'market'    => 'options.market',
        ],
        // Lifecycle callbacks — methods to call on the bridge at vendor lifecycle events
        'callbacks' => [
            'onCreated'  => ['setWebhook' => ['webhook_url']],
            'onUpdated'  => ['setWebhook' => ['webhook_url']],
            'onCanceled' => ['cancelFromFleetbaseOrder' => []],
        ],
    ];
}

Descriptor Fields

FieldPurpose
name / codeDisplay name + URL-safe machine code
host / sandboxBase URLs for production / sandbox
bridgeFQCN of your bridge class
svc_bridgeOptional — class providing ::all() for selectable service types
iso2cc_bridgeOptional — class providing ::codes() for regional codes
credentialParamsForm fields rendered when adding the vendor
optionParamsAdditional non-credential config fields
bridgeParamsMaps IntegratedVendor attribute paths to your bridge constructor args
callbacksMethods to invoke on the bridge at onCreated, onUpdated, onDeleted, onCanceled

bridgeParams paths are resolved against the live IntegratedVendor model — 'credentials.api_key' reads $vendor->credentials['api_key'], 'sandbox' reads $vendor->sandbox, etc.

Upload a PNG named <your-code>.png to the platform's integrated-vendors/ asset directory (used by ResolvedIntegratedVendor::logo). For self-hosted instances, place it in assets/integrated-vendors/<code>.png.

4. Test the Integration

The frontend "Add Integrated Vendor" UI now lists your vendor. Adding it persists an IntegratedVendor record; from there:

use Fleetbase\FleetOps\Support\IntegratedVendors;

$vendor = IntegratedVendor::find('iv_xxx');
$bridge = IntegratedVendors::bridgeFromIntegratedVendor($vendor);

$quote = $bridge->quote(['pickup' => $pickup, 'dropoff' => $dropoff]);

Or from the order dispatch flow, Fleet-Ops will route orders assigned to this vendor through your bridge automatically.

Reference

Adding an Integrated Vendor | Fleetbase