feat(adapter-duffel): add Cars API (search → quote → book) v0.7.2#99
Merged
Conversation
Duffel Cars is the rental-car vertical alongside Flights and Stays.
Same Bearer auth and Duffel-Version: v2 header as the existing flight
methods, just under the /cars/ namespace.
Five new methods on DuffelAdapter:
- searchCars POST /cars/search
- quoteCar POST /cars/quotes
- bookCar POST /cars/bookings
- getCarBooking GET /cars/bookings/{id}
- cancelCarBooking POST /cars/bookings/{id}/actions/cancel
Three-step flow (search → quote → book), unlike the two-step flight
flow. Search is geo-coordinate based; the brief explicitly defers
IATA-to-coordinate conversion to a future iteration (DQ-C8). Adapter
accepts only `{ latitude, longitude, radius? }` per location.
New canonical types in cars-types.ts: CarRate, CarQuote,
CarBookResponse, CarSearchRequest/Result, CarLocation, CarSupplier,
CarDetails (with ACRISS code passed through verbatim), and the open
Money type. Wire types in the same file mirror the Duffel JSON shape
best-effort. Mapper in cars-mapper.ts uses decimal.js for all
amounts, narrows enums where the brief documents them, and falls
back to safe defaults for unknown values (unknown payment_type →
'postpaid' so we never mis-charge).
Shared private request() helper now accepts an optional AbortSignal
in its options bag — pre-flight check throws before dispatch. In-
flight cancel needs an upstream change to fetchWithRetry, same
caveat that exists on the Hotelbeds adapter.
MockDuffelAdapter gains a full mock three-step flow with two car
fixtures (Toyota Corolla compact, VW Tiguan SUV). Synthetic IDs
match the live prefixes (seh_, rae_, qut_, boo_). Mock state is
in-memory and shared across calls so search→quote→book chains
work without the network.
Domain knowledge captured in docs/knowledge-base/cars.md with 8
numbered DOMAIN_QUESTIONs (DQ-C1..C8) for items the brief leaves
open: radius upper bound, time-zone semantics, enum closure,
privacy-policy consent flow, card creation, inbound flight number
format, refund computation, and IATA-to-coordinate conversion. Per
the constitution, these are surfaced rather than guessed.
Tests: 18 new cases across cars.test.ts. Validates body shapes,
Bearer + Duffel-Version headers, /cars/ path routing, mapper
output, abort handling, 429 retry-then-error, payment_type fallback
for unknown values, and a full mock flow round-trip
(search → quote → book → get → cancel).
Bump @otaip/adapter-duffel 0.7.1 → 0.7.2 (single-package patch
bump per VERSIONING.md). Workspace-wide v0.7.2 release follows
separately.
Verification:
- pnpm exec vitest run packages/adapters/duffel — 58 passed / 3 skipped (sandbox-gated).
- pnpm exec eslint packages/adapters/duffel/src — clean.
- TypeScript strict — clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3 tasks
ntbpy
pushed a commit
to ntbpy/AI_Agent_otaip
that referenced
this pull request
May 11, 2026
fix Patch bump off v0.7.1. Three pieces: - TelivityAI#99 @otaip/adapter-duffel adds Cars (search → quote → book → get → cancel) under /cars/. Same Bearer auth, AbortSignal threading on request(). 8 open DOMAIN_QUESTIONs captured in docs/knowledge-base/cars.md. - TelivityAI#97 unbreaks the Release workflow's Count agents step that TelivityAI#93's CLI re-export indirection broke. Every push-to-main since TelivityAI#93 had been failing silently — that's why v0.7.1's tag was created but the workspace npm bumps lagged behind for a while. - TelivityAI#98 switches gh release create to RELEASE_PAT so the release: published event propagates to the Publish workflow. This is the first release that should auto-publish to npm without a manual workflow_dispatch. Workspace-wide bump: 17 package.json files 0.7.1 → 0.7.2 (@otaip/adapter-duffel was already at 0.7.2 from TelivityAI#99). Going forward, the next release is v0.7.3. See CHANGELOG.md for details. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Duffel launched Cars as a full API vertical alongside Flights and Stays. This PR extends
@otaip/adapter-duffelwith the rental-car surface — same Bearer auth, sameDuffel-Version: v2, just under the/cars/namespace. Patterned identically to the Hotelbeds Activities/Transfers extension (#90): one adapter class, new product surface.What's new on
DuffelAdaptersearchCarsPOST /cars/searchquoteCarPOST /cars/quotesbookCarPOST /cars/bookingsgetCarBookingGET /cars/bookings/{id}cancelCarBookingPOST /cars/bookings/{id}/actions/cancelThree-step flow (search → quote → book), unlike the two-step flight flow. Search is geo-coordinate based; the brief explicitly defers IATA-to-coordinate conversion (DQ-C8). The adapter accepts only
{ latitude, longitude, radius? }per location.The shared
request()helper now accepts an optionalAbortSignal(pre-flight check; in-flight cancel needs an upstream change tofetchWithRetry, same caveat the Hotelbeds adapter carries).Files
cars-types.ts(canonical + wire types —CarRate,CarQuote,CarBookResponse, etc., plus aMoneytype),cars-mapper.ts(decimal.js-backed wire→canonical mapping),__tests__/cars.test.ts(18 cases),docs/knowledge-base/cars.md.duffel-adapter.ts(5 methods +request()signal threading),mock-duffel-adapter.ts(full three-step mock flow + 2 car fixtures),index.ts(re-exports),package.json(0.7.1 → 0.7.2).Domain knowledge
docs/knowledge-base/cars.mdcaptures the vendor brief verbatim and enumerates 8 openDOMAIN_QUESTIONmarkers (DQ-C1..C8) for the gaps the brief leaves open:radiusupper boundpickup_time/dropoff_timetimezonecar.category/car.typeenum closureprivacy_policiesconsent flowinbound_flight_numberformat (IATA vs ICAO)Per the constitution's no-invent rule, these are surfaced rather than guessed. The adapter falls back to the safe choice where it must (e.g. unknown
payment_type→'postpaid'so we never mis-charge) and documents the assumption inline.Verification
pnpm exec vitest run packages/adapters/duffel— 58 passed / 3 skipped (sandbox-gated).pnpm exec eslint packages/adapters/duffel/src— clean.pnpm --filter @otaip/adapter-duffel typecheck— clean.Versioning
@otaip/adapter-duffel0.7.1 → 0.7.2 (single-package patch bump per VERSIONING.md). The workspace-wide v0.7.2 release follows separately, same shape as #92 / #96. The brief asked for a "next minor" bump (0.8.0); flagging that we're sticking with the patch-bump policy unless you want to ratify another exception like the v0.7.0 hotelbeds case — let me know.Out of scope (intentional)
POST /payments/cards) — separate Duffel API.🤖 Generated with Claude Code