Salta ai contenuti

@guestcraft/channex-client

Questi contenuti non sono ancora disponibili nella tua lingua.

MIT, portable Channex HTTP client, written by hand from provider documentation (ADR 0006). It uses injected fetch, Web Crypto and Zod; it has no GuestCraft domain, database or server dependency.

createChannexClient(options) creates a reusable client for one connection. Options are apiKey (required), environment, baseUrl, fetch, timeoutMs, getRetries, userAgent, onResponse, sleep, random and now. onResponse receives response headers for pacing; sleep, random and now make retry behavior injectable. Calls accept an optional AbortSignal. The defaults are production, a ten-second attempt timeout and two GET retries; baseUrl overrides the selected environment. channexBaseUrl(environment) returns an environment’s base URL (https://app.channex.io/api/v1 or https://staging.channex.io/api/v1).

Resource Operations
properties Paginated list, single-property get
bookingRevisions Pending feed, historical list({ page, propertyId?, limit? }) (5 per page by default, at most 100), get, acknowledge
roomTypes A property’s room types: list(propertyId)
bookings currentRevisionId(id) and account-wide futureRevisionIds({ page, departingFrom })
webhooks register({ propertyId, callbackUrl, secret, eventMask? }), unregister({ propertyId, callbackUrl, eventMask? }); ids/delete for separately fenced writes
ariSetup Property settings including channel availability rules and the ages of the property’s hotel policy (hotel_policy_id, then GET /hotel_policies/{id}: infant_max_age, children_max_age; hotelPolicy null without one), and paginated rate plans with their rate_mode, occupancy options and children_fee/infant_fee
ari Absolute updateAvailability and updateRestrictions (a rate plan’s rate, or per-occupancy rates with one rate per occupancy); accepted tasks and warnings. availability({ propertyId, from, to }) and restrictions({ propertyId, from, to, restrictions }) read current values per date, both ends included
tasks get asynchronous task outcome and channel events; list({ propertyId, page?, limit?, order? }) one page of a property’s processed tasks (Channex lists processed tasks only; a listed task without finished_at is refused)
messages Installations, Live Feed/events, threads, messages, sending, attachments and no-reply status
stripeTokenization The Stripe Tokenization App’s one documented operation, paymentMethod(bookingId) (POST /bookings/{booking_id}/stripe_payment_method: the Stripe payment method of the booking’s card, read from data.token; 403 without the app on the booking’s property). The committed contract documents no install, listing or removal of this app: the operator installs it in Channex’s interface
bookingReports Booking Reporting: noShow(id, { waivedFees, penaltyAmount? }) (a non-negative decimal-string penalty, Expedia only, checked before sending), invalidCard(id) and cancelDueInvalidCard(id) sent without a body; each resolves on Channex’s success message
@guestcraft/channex-client/webhooks verifyWebhookSignature, keyLabel, parseWebhookDelivery

The client does not expose hotel administration, channel setup or the whole Channex API. A transport method alone does not implement booking ingestion, synchronization or a GuestCraft operator workflow.

verifyWebhookSignature checks x-hmac-timestamp, x-hmac-signature-256 and x-hmac-keys-256: HMAC-SHA256 covers {timestamp}.{raw body}, with a default 300-second tolerance. It accepts current and pending secrets, matched by keyLabel (... plus the secret’s last six characters). parseWebhookDelivery never throws; its optional payload represents data sent with send_data enabled. GuestCraft’s server intake uses neither helper: it authenticates the custom X-GuestCraft-Channex-Secret header.

Responses are validated and undeclared fields are dropped, including raw OTA fields. A revision’s guarantee is reduced to an allowlist: the card type (a documented BookingRevisions.CardType code, otherwise unknown), last4 (the last four characters of Channex’s masked number when they are digits), the expiry (MM/YYYY or MM/YY, otherwise null), is_virtual and the virtual card’s documented meta values. The number, security code, holder’s name and token are never kept; a guarantee without the documented shape is null and never rejects the revision; rejected-revision placeholders carry no card data. Revision parsing returns a validated revision, a rejected safe identity envelope, or an unreadable marker. A consumer must durably save or quarantine an owned revision before acknowledging it; unreadable markers must not be acknowledged. Cosmetic customer fields may be null or omitted. See the booking import contract.

Revision history supports a property filter. bookings.futureRevisionIds reads the account-wide current-booking list and is suitable only for an exclusive account. Webhook matching requires the documented relationships object and checks its property, callback and event mask (default: booking); attribute-only legacy lists fail with invalid_response before any write. Registration sets active=true, global=false and send_data=false with a custom authentication header. Deletion tolerates a concurrent 404. Messages installations are discovered account-wide and filtered locally to the requested property; request-decision writes are absent.

ChannexError exposes kind, retryability, optional pacing delay and unknown write outcome, plus the provider error’s code, title and details when present, and a transport cause for network failures/timeouts. Error message text omits request and response bodies; the exposed fields and cause are not redacted. Only GETs retry network errors, timeouts, HTTP 408 and 5xx responses. Writes and 429 responses are not retried by the client. Collection readers that gather all pages are bounded to 50 pages of 100 and refuse partial results. An accepted ARI task does not prove delivery to every channel.

Terminal window
pnpm --filter @guestcraft/channex-client test
pnpm --filter @guestcraft/channex-client typecheck

Tests use scripted transports, synthetic fixtures and the independent simulator. Webhook signature vectors are computed independently with Node crypto. Offline captured request/response contracts live in channex-simulator; they do not use client schemas. The optional upstream drift suite needs the public specification in .cache/: pnpm channex:spec fetches it without an account. Without that cache the drift and Messages contract tests report skipped; the nightly channex-drift workflow runs both. The drift comparison checks the fields the client reads, their nulls, value types and enums, and tuples member by member against OpenAPI prefixItems. Ordinary tests require no customer credentials. The full specification is not committed; see ADR 0006 for the specification and snapshot policy.