Connecting your TMS

The chargeback defense workflow depends on one thing: a shipment record (verify_ai_shipments) that the verification can join to via bol_number. You can get those rows in three ways — via a managed TMS adapter, by pushing shipments through the API yourself, or by tagging them inline on each /api/v1/verify call.

This guide covers the four adapters we ship (or plan to ship), and the contract for writing your own.

Adapter status at v1

| TMS | Adapter | Status | | ------------------ | ---------------------------------------------------- | --------------------------------------------------------------------- | | McLeod LoadMaster | lib/integrations/runtime/mcleod.ts | Preview — stub. Typed contract defined; all methods throw. | | SupplyPike | lib/integrations/runtime/supplypike.ts | Preview — stub. Outbound submit + inbound webhook both deferred. | | Cargowise | — | Roadmap — v1.1. | | MyEZclaim | — | Roadmap — v1.1. |

What 'stub' means here

Each adapter file exposes the expected TypeScript surface and a factory (createMcLeodAdapter / createSupplyPikeAdapter), but every method throws Error("Not implemented: ... deferred to the post-merge Cluster 3 framework"). Neither adapter authenticates against the live vendor APIs yet, and they are not registered with the Cluster 3 integration framework (lib/verify-ai/integrations/registry.ts). Until they graduate, populate verify_ai_shipments directly (service-role client) or tag metadata.bol_number on captures so draftDispute() can join when it's eventually called.

McLeod LoadMaster

McLeod is the dominant TMS for North American truckload carriers and a common one for asset-based brokers. Their integration surface is the LoadMaster REST API plus the IFS (Integrated File System) feed for higher-volume shippers.

Auth is OAuth2 client-credentials. The expected per-customer config (stored on verify_ai_integrations.config once the adapter moves into the Cluster 3 framework) is:

json
{
  "base_url": "https://acme.loadmaster.com/lme",
  "client_id": "<oauth2 client>",
  "scope": "ifs.read ifs.documents"
}

The client_secret lives in Supabase Vault, referenced by verify_ai_integrations.credentials_ref.

Sync model. The runtime contract today (per lib/integrations/runtime/mcleod.ts) exposes three calls:

ts
const adapter = createMcLeodAdapter(config);
await adapter.getShipments(since);     // since: Date — list shipments
await adapter.getShipment(bolNumber);  // single lookup
await adapter.getDocuments(orderId);   // POD / BOL document URLs

All three throw "Not implemented" at v1. When the adapter graduates, a scheduled cron is expected to call getShipments() nightly per customer and upsert into verify_ai_shipments keyed on (customer_id, bol_number). The dispute drafter consumes the same rows. Until then, McLeod customers should either push shipments to verify_ai_shipments directly or rely on capture-time tagging.

SupplyPike

SupplyPike is the dispute-management layer many shippers already use for Walmart and Amazon Vendor deductions. The integration is designed to go both directions; both directions are stubbed today.

  • Outbound — VerifyAI submits drafted disputes (verify_ai_chargeback_disputes rows with status: "draft") to SupplyPike for processing. Preview — stubbed at v1. The runtime contract (lib/integrations/runtime/supplypike.ts) is:

    ts
    const adapter = createSupplyPikeAdapter(config);
    await adapter.createDispute(payload);     // submit new dispute
    await adapter.getDispute(disputeId);      // poll status
    await adapter.listDeductions(since);      // pull new deductions
  • Inbound — SupplyPike will eventually post adjudication outcomes to POST /api/v1/integrations/supplypike/webhook, signature-verified with X-SupplyPike-Signature (HMAC-SHA256). The handler will match on supplypike_dispute_id (the column already exists on verify_ai_chargeback_disputes) and flip status to won / lost / partial / expired. The route is not implemented at v1 — it lands together with the outbound adapter graduation.

Auth (when the outbound adapter ships) is a per-tenant Bearer API key. The expected config shape:

json
{
  "base_url": "https://api.supplypike.com/v1",
  "tenant_id": "<supplypike tenant uuid>"
}

For customers without SupplyPike, render the dispute letter via chargeback defense and submit manually through whatever portal the retailer uses.

Cargowise and MyEZclaim (roadmap)

Cargowise (WiseTech) covers global freight forwarders and a significant slice of LTL operators. MyEZclaim is the dominant SaaS for OS&D claims management in North American freight.

Both are on the v1.1 roadmap. The interface for each will match the McLeod / SupplyPike pattern: a syncShipments call to pull load data, plus push hooks for disputes and claims. If you have either deployed and want to be a launch partner, send a note to support — we prioritize adapter rollout by signed pilots.

V1: bridging without an adapter

Until McLeod and SupplyPike graduate from preview, here's the recommended pattern for any customer with chargeback exposure:

Option 1 — Tag inline on capture

Pass metadata.bol_number on every /api/v1/verify call. The verify pipeline simply records the value on the verification row — no shipment lookup happens at capture time. When you later call draftDispute(verificationId), the drafter reads the metadata and joins by (customer_id, bol_number).

bash
curl -X POST https://verify.switchlabs.dev/api/v1/verify \
  -H "X-API-Key: vai_your_api_key" \
  -F "image=@pallet.jpg" \
  -F "policy=pol_damage_detect" \
  -F 'metadata={"bol_number":"BOL-44128301","po_number":"PO-9912204"}'

You can populate bol_number automatically by running the BOL OCR endpoint on a snapshot of the paperwork.

Option 2 — Insert shipments directly

Populate verify_ai_shipments from your service-role client. Either feed it from a nightly export out of your TMS, or trigger inserts on dispatch events from your warehouse system. The first-class columns are customer_id, bol_number, pro_number, retailer, ship_date, deliver_date, origin (jsonb), destination (jsonb), status, and metadata (jsonb) — extras like MABD, PO number, and DC code live under metadata. A minimal insert:

json
{
  "customer_id": "<uuid>",
  "bol_number": "BOL-44128301",
  "retailer": "walmart",
  "ship_date": "2026-05-15",
  "deliver_date": "2026-05-20",
  "metadata": {
    "po_number": "PO-9912204",
    "ship_to_dc": "DC-6094",
    "mabd": "2026-05-20"
  }
}

The drafter performs the join at draftDispute() time — there is no automatic verification-to-shipment binding event in the system. A REST endpoint for shipment writes is not part of v1; admins go through the service role today.

Writing a custom adapter

Two related interfaces live in the tree today; pick the one that matches what you're building.

1. Outbound integration framework (lib/verify-ai/integrations/). This is the Cluster 3 framework shared with claims platforms (Guidewire, Duck Creek, Mitchell). It is the canonical home for adapters that push verification events into a third-party system. The interface in lib/verify-ai/integrations/types.ts is:

ts
export interface IntegrationAdapter {
  readonly type: string;   // matches verify_ai_integrations.type
  readonly label: string;
  pushVerification(
    integration: IntegrationConfig,
    event: OutboundEvent,
  ): Promise<DeliveryResult>;
  testConnection?(integration: IntegrationConfig): Promise<DeliveryResult>;
}

Adapters call registerAdapter(adapter) from ./registry, and side-effect imports live in lib/verify-ai/integrations/index.ts (see guidewire/adapter.ts for a reference stub). Use this path when you want a verification event to fan out to a TMS in real time.

2. Runtime TMS adapters (lib/integrations/runtime/). This is the standalone surface the McLeod and SupplyPike stubs live on today. There is no shared base interface yet — each adapter exports its own typed contract plus a factory:

ts
// lib/integrations/runtime/<your-tms>.ts
export interface YourTmsConfig { /* ... */ }
export interface YourTmsAdapter {
  getShipments(since: Date): Promise<unknown[]>;
  // + any TMS-specific calls
}
export function createYourTmsAdapter(config: YourTmsConfig): YourTmsAdapter { /* ... */ }

There is no lib/integrations/runtime/index.ts factory file yet — callers import the per-vendor module directly. The McLeod and SupplyPike stubs document the API surfaces they expect to wrap when they graduate; mirror that structure for new vendors.

The Cluster 3 merge agent is expected to move both McLeod and SupplyPike under lib/verify-ai/integrations/runtime/ and register them with the framework registry above.

Operational prerequisites

A few platform-side prerequisites apply to any TMS integration that's going to scale:

  • Webhook signing secrets — every inbound TMS webhook needs its own secret, stored on the customer's integration config and verified per request. The SupplyPike inbound contract is HMAC-SHA256 against X-SupplyPike-Signature.
  • Backpressure — high-volume customers (>10k shipments/day) should run the sync hourly rather than daily and consider a message queue between TMS pulls and the shipments table.
  • PostGIS is a prerequisite for the separate Regulator Dashboard work (Cluster 4.2 geofence joins), not for TMS integrations. If you plan to enable both, run docs/operations/postgis-check.md once before either ships.

What's next

Get in Touch

Questions about pricing, integrations, or custom deployments? We'd love to hear from you.