# ADR-122 Booking-Point Abstraction for Multi-Account / Multi-Carrier Extensibility

## Status

Proposed, 2026-07-27.

## Status History

```yaml
status_history:
  - date: 2026-07-27
    status: Proposed
    changed_by: hkl
    reason: Initial proposal — extensible dispatch origin abstraction for DispatchWithAutomation.
    changed_via: adr-kit (360lm) — authored from DL CLI via SSH
```

## Context

Today all bookings go through one Shiprocket account (YEMO). The owner has named concrete future
origins: a second Shiprocket account for 360DL (`business@360degreelogicalmktg.com`), a **Blue Dart
direct B2B API** account, and the retained **manual (non-API)** path from the existing dispatch PWA
(Phase 1 per §19.5). Hard-coding "Shiprocket-YEMO" everywhere would force a rewrite when the second
account or the direct-carrier API arrives. But full multi-tenant credential management now would be
premature (only one account is live).

## Decision

Introduce a first-class **`booking_point`** abstraction: a named dispatch origin with a `kind`
(`shiprocket_api` | `carrier_direct_api` | `manual`), its own credential handle (Docker secret via
`shiprocket-proxy`, ADR-117), and capability metadata (which couriers/services it can book). Every
`dispatch` shipment records the `booking_point` it went through. v1 seeds exactly one row
(`shiprocket-yemo`) plus the `manual` point; adding the 360DL Shiprocket account or the Blue Dart
direct API later is a **new row + a proxy adapter**, not a schema change. The booking rules engine
(ADR-119) selects couriers *within* the chosen booking-point(s).

**Decision Maker:** hkl

## Implementation Notes

- **Table:** `dispatch.booking_points(id, name, kind, courier_capabilities jsonb, active, created_at)`; credentials keyed by `booking_points.id` in the proxy's secret store — never in the DB.
- **Proxy adapters:** one adapter module per `kind` in `shiprocket-proxy`; a common interface (`get_rates`, `check_serviceability`, `book`, `assign_awb`, `track`) so the rules engine is adapter-agnostic.
- **v1 seed:** `shiprocket-yemo` (kind `shiprocket_api`) + `manual` (kind `manual`, records externally-obtained AWB/courier/cost). 360DL Shiprocket + Blue Dart direct = future rows.
- **Find sites:** `grep -rn "booking_point" /var/www/360lm/dispatch /opt/shiprocket-proxy`

## Alternatives Considered

- **Hard-code the single Shiprocket account.** Rejected: guarantees a rewrite when the 2nd account / direct API arrives (both already named by the owner).
- **Full multi-tenant credential vault now.** Rejected: premature for one live account; the booking_point row + per-id secret gives the seam without the overhead.

## Consequences

**Positive:**
- New origins drop in as data + an adapter, no rewrite; manual and API paths coexist uniformly; per-shipment provenance for analytics.

**Negative / Trade-offs:**
- A small indirection layer built before it's strictly needed (one active point in v1).

**Risks and mitigations:**
- *Adapter interface too narrow for a future carrier API* → keep the interface minimal + versioned; extend when the Blue Dart adapter is actually built.

## Related Decisions

- ADR-117 (proxy holds per-point creds), ADR-119 (rules select within points), ADR-105 (proxy auth), ADR-106 (grants).

## References

- `/var/www/360lm/docs/MDD_dispatch_automation.md` §13, §15, §19.5.
