Skip to content

0006. Write the Channex client by hand and test it against a simulator

  • Status: accepted, revised with the first implementation
  • Date: 2026-10-26

Everything GuestCraft sells goes through Channex (analysis, chapters 3 and 11). Channex has no official SDK. It publishes an OpenAPI 3.1 document at https://openapi.channex.io/public/openapi.json (143 operations), but the document declares no license, and its coverage is incomplete: the Channel API, message threads, one-time tokens and webhook payloads are missing. A client generated from it could not be redistributed under MIT with certainty, and tests generated from the same document would only check the client against itself.

  • packages/channex-client (MIT) is written by the project. For each operation GuestCraft uses, it has a Zod schema for the request and the response, written from the documentation and checked against the published specification. Responses are validated at the boundary: undeclared fields are dropped, so that card data and raw OTA messages never enter GuestCraft; fields that decide behaviour, such as the status of a revision, are strict enums, so that an unknown value stops processing with a clear error instead of being misread; informational fields accept any string.
  • The transport takes an injected fetch, a base URL (production or staging) and the API key, and handles timeouts, cancellation, retries only where the operation is safe to repeat, 429 with Retry-After, and typed errors that say whether a failure is retryable.
  • The specification is not committed. A script downloads it with a pinned SHA-256 into an ignored cache, and a drift test compares the client schemas with it. If the file changes, the test fails until a maintainer reviews the difference and updates the pin.
  • An offline simulator response contract uses a dated projection of the published API facts (field names, constraints and operation signatures), with source hash and no upstream prose/examples. pnpm channex:snapshot regenerates it; the nightly drift workflow checks it stays current. The simulator is hand-written and never imports the projection. This conformance check is independent of the client schemas; the full upstream document and generated clients remain uncommitted pending license clarification.
  • packages/channex-simulator (MIT) models the Channex behaviours that GuestCraft depends on (revision feed and acknowledgements, ARI writes, rate limits per property and endpoint, failures, lost responses), independently of the client code. It runs in process, as a fetch implementation, and as an HTTP server for end-to-end tests.
  • Fixtures are synthetic and carry their origin; no real guest, card or token data enters the repository.
  • The question “may the specification be redistributed, and under which license?” is asked to Channex in writing. If the answer is yes, the specification is committed and the client may be generated.
  • More code to write than a generated client, but every schema is reviewed, typed for the operations GuestCraft calls, and owned by the project.
  • Three independent checks: fixtures (pull requests, offline), the simulator (pull requests, offline) and a canary against Channex staging (nightly, with credentials).
  • The drift test needs network access to openapi.channex.io; without it the test reports “not run”.
  • Generate the client with Hey API from the published specification. Rejected until the license is clear, and because the specification misses several endpoints GuestCraft needs.
  • Mock Channex with MSW handlers generated from the specification. Rejected as the main oracle: the mocks would share the client’s assumptions. MSW remains available for interception in specific tests.