Last updated

Changelog

All notable changes to this project will be documented in this file.

Partner API (core)

[5.20.0] - 2026-10-29

Added

  • store booking source (5.20.0): a booking's source can now be store, a guest purchase in your embedded store (<store id>.hubbyesim.app). You read these bookings back through your booking endpoints, as before. source is an open enum (see the Native eSIM Integration guide, section 10, "Versioning"), so code with a default branch already handles the new value. No shape changes, and no new refusals.
  • @hubbyesim/sdk 2.7.0 speaks Partner API 5.20.0. BookingSource gains store.

[5.19.0] - 2026-10-26

Not additive

A new Partner API minor is additive, except for the changes its changelog entry names. These are the changes 5.19.0 names: after each one, a request that succeeded before can be refused. Read them before you upgrade.

  • A starter issued and then cancelled counts toward the Starter limit (5.19.0): a traveler who holds two such starters for one destination from you is refused a third. POST /v1/bookings and the package-specification updates answer 422 validation_failed on package_type. The issue call answers 422 validation_failed on external_user_id, or 409 starter_cap_exceeded.
  • A starter upgraded to another package type counts toward the Starter limit (5.19.0): the refusals are the same as above.

Changed

  • The Starter limit is two per destination (5.19.0): a traveler can have at most two Starter packages per destination from you. Before, the limit was one live Starter package per traveler. A second starter for the same traveler and destination, a starter for another destination, and a starter for a traveler whose other starters came from another Partner are now accepted. An update that moves a starter to a destination that already holds two is refused. The single package-specification update declares its 422. The Native eSIM Integration guide (section 4, "Starter") states the rule.
  • @hubbyesim/sdk 2.6.0 speaks Partner API 5.19.0, and so takes in 5.18.0 too. getCompatibleDevices takes an optional filter object before the request options: getCompatibleDevices({ brand: 'Samsung' }). A call that passed RequestOptions as the first argument now passes them second: getCompatibleDevices({}, { signal }). New types: DeviceCompatibilityStatus, DeviceCompatibilityDevice, SupportedDevice, DeviceCompatibilityListFilters and InstallInstructionApnSetup.

[5.18.0] - 2026-10-26

Not additive

A new Partner API minor is additive, except for the changes its changelog entry names. These are the changes 5.18.0 names: after each one, a value you already read can change. Read them before you upgrade.

  • is_compatible is false for a phone that is not on the list (5.18.0): POST /v1/device-compatibility answered is_compatible: true for every device. It now answers true only when status is supported.
  • The install instructions come from the Hubby web app (5.18.0): GET /v1/esims/{iccid}/instructions returns the copy of the web app's guided setup.
    • min_os_version is removed from every install set.
    • The on_arrival order can be different for each family: on Samsung and Xiaomi, mobile_data comes before roaming. Find a section by id, not by position.
    • On Xiaomi, one_tap.install_link is null: that phone has no one-tap install, and the one-tap steps are the manual steps.
    • The iPhone manual section is localized and has a clip, as on Android.
    • The copy exists in English, German, Spanish and Swedish. Any other locale gets English, and locale still echoes the locale you asked for.
    • esim_label is your Partner name followed by eSIM. A visual identity no longer has an effect on it, and the Hubby eSIM fallback is gone.
    • The Android families are listed pixel, samsung, xiaomi (before: samsung, pixel, xiaomi). Find a family by family, not by position.

Added

  • Device compatibility is a real lookup (5.18.0): POST /v1/device-compatibility adds status (supported, unsupported or unknown), note (regional limits, or null) and device (the matched brand, name, model and year, or null for unknown). The match is on brand + device name only. GET /v1/device-compatibility lists the Supported devices, ordered by brand, then name, with an optional brand filter; before, the list was always empty. The Native eSIM Integration guide (section 6, "Device compatibility") shows both calls.
  • APN setup in the install instructions (5.18.0): every install set has apn. Pixel, Samsung and Xiaomi carry {apn, title, clip_url, steps}, the steps to add the access point name by hand for an eSIM that has signal but no internet. The iPhone has null.

[5.17.0]

Not additive

A new Partner API minor is additive, except for the changes its changelog entry names. These are the changes 5.17.0 names: after each one, a request that succeeded before can be refused, or a value you already read can change. Read them before you upgrade.

  • The catalog reads follow your destination list (5.17.0): when Hubby set up a destination list for your account, GET /v1/destinations, GET /v1/destinations/{destination}, GET /v1/destinations/{destination}/bundles and GET /v1/bundles answer only the destinations on it, the same set your bookings accept. A destination outside the list answers 404 not_found on the single reads. With no list, every active destination is answered, as before.
  • destination must match exactly (5.17.0): POST /v1/bookings and the package-specification updates accept the ISO alpha-2 or alpha-3 code, a region's code or the English name, in any letter case. A translated name, a typo or any other value answers 422. A specification update whose destination matches nothing answers 422; before, it removed the destination from the item.
  • A repeat after a cancel books a new booking (5.17.0): a cancelled booking no longer holds its external_reference and depart_at. The same pair after DELETE /v1/bookings/{id} makes a new booking; before, it answered the cancelled booking. A read by reference answers the newest booking.
  • A GET ignores the Idempotency-Key (5.17.0): nothing is stored and nothing is replayed, and a GET never answers 409 idempotency_key_in_use. POST, PATCH and DELETE keep the key.
  • A spent issue key stays spent for the life of the item (5.17.0): after a terminate, the key that issued the package answers 409 idempotency_key_spent also after 24 hours. Issue again with a new key.
  • An end by Hubby or the provider returns the item to queued (5.17.0): when Hubby or the network provider ends a package you issued before it activated, the item returns to queued and the issue key is spent, as after your own terminate. Read the item's state and issue again with a new key. An item the traveler redeemed in the Hubby app does not change.
  • POST /v1/redirect-tokens answers 503 and 409 (5.17.0): while Hubby's previous platform cannot answer, the call answers 503 bridge_unavailable with Retry-After: 30, and nothing is created; before, it answered a bare 500. A record from the previous platform that cannot be imported answers 409 legacy_graph_not_importable. Retrying gives the same answer; contact Hubby. The operation also declares 422 validation_failed.
  • A Native account always books a destination (5.17.0): for an account with end_customer_channel: partner_app, force_destinationless is never true; Hubby refuses the combination. A package specification with neither destination nor bundle_id is refused at booking: POST /v1/bookings answers 422 validation_failed on package_specifications.N.destination, and the legacy /api booking call refuses it at booking too. The issue answers 422 destination_required only for an item booked without a destination: that happens only while force_destinationless is true (never on a Native account) or when Hubby books the item for you.
  • A Native account's webhook endpoint gets the default signed body (webhook contract): for an account with end_customer_channel: partner_app, Hubby refuses an endpoint with the flat body, with a payload template or without a secret, and ignores a flat body or template stored before.
  • A Native account's booking events carry no code or uuid (webhook contract): for an account with end_customer_channel: partner_app, booking.created and booking.cancelled carry each item as {id}. Other accounts keep code and uuid.
  • Every webhook destination field sends the legacy code (webhook contract): on the package and top-up events, booking.created packages[].destination, destinations[] and destination_details[].iso3, a country is its ISO alpha-3 code (NLD) and a region is its code (EU, XCA). A region line is no longer missing from booking.created. A multi-country booking from the legacy API can also send XMS, XMD, XUD, XJK, XUM and XSI, besides EU, XN, XA, XS, OC, AF, XCA and XME.
  • A data entry's validity is the end of its queue (5.17.0): on GET /v1/esims/{iccid}/usage, a data entry with a running package and a waiting package has a valid_until date (before: null), and remaining_seconds includes the remainder of the running package. An entry with only running packages does not change.

Added

  • One answer shape for the package-specification updates (5.17.0): PATCH /v1/bookings/{bookingId}/package-specifications and PATCH /v1/bookings/{bookingId}/package-specifications/{uuid} always answer {updated_count, skipped_count}. skipped_count is 0 when nothing was skipped and on the single-item call. uuid is a plain string.
  • Declared refusals (5.17.0): the issue call declares 422 destination_required and no_free_esim_available, 409 provisioning_target_changed and package_queue_not_redeemable, and a 500 that is safe to retry with the same Idempotency-Key. The single package-specification update declares 400 package_specification_already_redeemed. The presigned upload's 409 carries code upload_already_exists, and the upload declares its 422 and its 429 with Retry-After. GET /v1/bookings and POST /v1/redirect-tokens declare 422 validation_failed.
  • X-Hubby-Call-Id on every response (5.17.0): the reference declares the header on every response of every operation, and its introduction names the answers that carry none (a refusal before routing).
  • Reference details for generated clients (5.17.0): the iso3 filter on GET /v1/destinations is declared. The request package_type lists data_limited, data-limited, starter and unlimited. Bundle.type uses the PackageType list, which has three values: data_limited, starter and unlimited. A nullable field that points to a schema keeps its description, so a generated client keeps the comment.

Changed

  • The create-booking schema drops five refused fields (5.17.0): departure_date, return_date, custom_branding, package_specifications[].esim_type and package_specifications[].expires_at are no longer in CreateBookingRequest. The API has refused them with 422 since 5.0.0 and still does.
  • Reference corrections (5.17.0): CreateBookingRequest.data is a free object, stored and never returned, not a list of strings. communication_options is nullable and its channels is optional on both booking bodies. date_of_birth is a date, not a date-time. The GET /v1/bookings filters depart_at, created_at, departure_from and departure_to are dates (YYYY-MM-DD). No wire change.
  • Descriptions tell the truth (5.17.0): every other change is a description written for a Partner. The webhook text in the reference and the Native eSIM Integration guide agree: Hubby sends one signature, a secret change is a hard cutover, retries last about 3.4 hours, package.first_usage fires at 10 MB of use, and esim.location_change arrives 15 to 75 minutes after the attach. No wire change.
  • @hubbyesim/sdk 2.5.0 speaks Partner API 5.17.0. Its README and types name the changes above. PackageSpecificationUpdateResponse is one shape with skipped_count, and CreateBookingRequest no longer carries the five refused fields. A GET answered 503 legacy_booking_unavailable or install_instructions_unavailable is retried, after Retry-After when the answer carries one; a POST, PATCH or DELETE answered 503 is not repeated, and bridge_unavailable is not retried. The README error table adds 502 package_termination_refused, 503 install_instructions_unavailable, 503 legacy_booking_unavailable, 503 bridge_unavailable, 409 legacy_graph_not_importable and 422 idempotency_key_reused, and says that a secret change is a hard cutover.

[5.16.0] - 2026-10-19

Added

  • Idempotency on every write operation that honours it (5.16.0): the reference declares the optional Idempotency-Key header, the 409 idempotency_key_in_use with Retry-After, the 422 idempotency_key_reused, and Idempotent-Replayed: true on a replayed success, on the POST, PATCH and DELETE operations that accept the key. A GET ignores the key: nothing is stored and nothing is replayed, and the reference declares none of this on a GET. Where an operation already listed a 409 or a 422, its body is now one of two shapes (anyOf): for example, the 422 of POST /v1/bookings is the validation body or {"message", "code": "idempotency_key_reused"}. Branch on code.
  • iso2, iso3 and slug on GET /v1/destinations/{destination} (5.16.0): the single-destination read carries the same codes as the list. Its schema has the stable name DestinationDetail (before: DestinationResource). The admin fields tier, is_active, sort_order, click_count, created_at, updated_at, created_by and updated_by are described as internal: do not build on them.
  • "Set up by Hubby for your account" in the Native eSIM Integration guide: the settings only Hubby sets (end_customer_channel, package_strategy, force_destinationless, the default eSIM label, emails to your travelers) and how to check them with GET /v1/me. The guide also states the package order on one eSIM, all rate limits (including presigned upload and analytics), that no device compatibility check exists yet, and how the instruction language falls back.

Changed

  • The package-specifications update is two operations (5.16.0): PATCH /v1/bookings/{bookingId}/package-specifications changes every item not yet issued, and PATCH /v1/bookings/{bookingId}/package-specifications/{uuid} changes one item. uuid is the item's id in Booking.items. The behaviour does not change.
  • Descriptions tell the truth (5.16.0): communication_options and its channels are stored and returned; they do not control Hubby's messages. Hubby emails a traveler only when Hubby has turned on emails for your account, by agreement, and a date change then moves the pre-departure emails. The instructions locale and esim_label fall back from the query to your latest booking that issued a package on the eSIM, then your defaults, then en-US and Hubby eSIM. The eleven internal Bundle fields read "Internal; not for Partner use; do not build on it." No wire change.
  • @hubbyesim/sdk 2.4.0 speaks Partner API 5.16.0. updatePackageSpecifications calls the published path without an item uuid. The DestinationDetail type is new; DestinationResource is a deprecated alias of it. A generated error body is a union where the reference lists two shapes. The shipped types and signing-vectors.json carry only Partner-facing text.
  • The webhooks page holds only the contract you implement (webhook contract, no API version): events, set-up, delivery and retries, envelope, headers and signature, and one field table per event. Every delivery attempt is signed again with the time it is sent, so a retry stays inside your timestamp tolerance. The x-hubby-signature header can hold several sha256= values; one match is enough.
  • Legacy guides grouped: the legacy-only guides, use cases and tutorials are in the Legacy API group of the sidebar, each with the legacy banner. Their URLs do not change. The legacy Authentication guide says that legacy keys expire after 5 years.
  • Webhook package events name their own booking (webhook contract, no API version): on package.activated, package.first_usage and package.usage.*, booking_id is now the reference of the booking that bought the package, or null when that booking has none. For a top-up it is the top-up's booking; before, it was the booking the eSIM was first claimed with. package.terminated already named its own booking.

[5.15.0] - 2026-10-19

Added

  • Operating facts in the Native eSIM Integration guide: the default rate limits per Partner (issue 30 per minute; GET /v1/esims/{iccid} and GET /v1/esims/{iccid}/usage with refresh=true share 60 per minute; reads without refresh have no limit), when is_cancellable is true for a booking with no depart_at, why the top-up rule is in your app and not in the API, and which keys to use: a legacy integration keeps calling api.hubbyesim.com with its legacy keys, and the legacy backend forwards those calls to core; to call core directly, use the keys Hubby creates for you on core. The FAQ says that API keys do not expire unless Hubby sets an expiry on the key.

Changed

  • Booking semantics, stated once (5.15.0): items is required on every booking read in the reference. BookingItem.status is deprecated; read state. The reference and the Native eSIM Integration guide (sections 4 and 8) now state the external_reference rule (unique per Partner and depart_at; the same pair returns the existing booking; never merged), the GET /v1/me settings that refuse a bundle_id with 422, when size and package_duration are required, how a booking locale sets the instruction language, and which read answers which item state. No behaviour changes.
  • Region codes and the sellable catalog (5.15.0): a region item's destination is the region's code (EU, XA, XN, XS, OC in iso2; XCA, XRC, XME in iso3), the same code GET /v1/destinations lists, so you can map every booking item back to the catalog. GET /v1/bundles and GET /v1/destinations/{destination}/bundles answer only sellable bundles (active, visible and priced for you). The internal bundle fields (label, provider, package_template, partner, traffic_policy, is_active, is_visible, priority, deleted_at, created_by, updated_by) are described as internal: do not build on them. Read the throttle of an unlimited bundle in fair_use_throttle. The Native eSIM Integration guide (section 3) shows the daily ETag sync, which @hubbyesim/sdk 2.3.0 does with getBundles({ etag }): an unchanged catalog resolves to { notModified: true, etag } instead of an error. No wire change.
  • @hubbyesim/sdk 2.3.0 speaks Partner API 5.15.0. 2.3.0 is no longer on npm (unpublished on 2026-10-19); install 2.4.0 or later. createBooking(input, { idempotencyKey }) takes your own Idempotency-Key, so a retry after a crash between booking and saving does not book twice. Booking.items is required in the types. The README documents every method.
  • Webhook remaining_days counts the validity left (webhook contract, no API version): on the package.usage.* events it is the calendar days from today to activation plus the package's duration, never below 0, and null before activation. Before, it counted the days to expires_at, the activation deadline.
  • Webhook package.activated carries elapsed_days and remaining_days (webhook contract, no API version): at activation elapsed_days is 0 and remaining_days is the package's whole duration in calendar days. remaining_days is null for a package with no duration.
  • The Partner reference is titled "Hubby Partner API" (5.15.0). Before, the generated document was titled "Hubby". Some operation summaries are rewritten to describe what the operation does for you. No wire change.
  • Webhook package.terminated carries your reference as booking_id (webhook contract, no API version), as every other package event does. booking_reference keeps the same value. Hubby's own booking id is no longer sent on this event.
  • Webhook booking_id is your reference on every event (webhook contract, no API version): package_queue.claimed, promo_code.redeemed and upgrade_offer.purchased now send your external_reference as booking_id, or null when the booking has none. Hubby's own booking id is no longer sent on these events.
  • Legacy pages grouped: the Quickstart and the Authentication Guide describe the legacy API on api.hubbyesim.com. They are now in the Legacy API group of the sidebar, and each starts with a banner that points to the Native eSIM Integration guide.
  • The Native eSIM Integration guide no longer promises a Postman collection. The OpenAPI document is the published contract.

[5.14.0] - 2026-10-19

Added

  • eSIM usage per destination (Partner API 5.14.0, @hubbyesim/sdk 2.2.0 getEsimUsage): GET /v1/esims/{iccid}/usage answers what the traveler can still use, one DestinationUsage entry per destination, newest first, with destination, is_unlimited, total_bytes, used_bytes, remaining_bytes, valid_until, remaining_seconds, package_ids and synced_at. Packages for one destination add up into one entry; packages bought in the Hubby app count too. The Native eSIM Integration guide shows how to draw one card per entry.
  • destination on every package of GET /v1/esims/{iccid} (5.14.0): a DestinationSummary (id, name, type, iso2, iso3). A bundle's destination in the catalog is the same object and gains name and type.
  • GET /v1/destinations and GET /v1/destinations/{destination} in the reference under the Native eSIM Integration tag, with the Destination, DestinationResource and DestinationSummary schemas. Today: both operations are in the Partner API 5.x (core) reference under its Destination tag, and the single-read schema is DestinationDetail (since 5.16.0); the document has no Native eSIM Integration tag.

Changed

  • Response enums are open (5.14.0): a minor release may add a value; handle one you do not know. A breaking change ships as a new URL major, and /v1 is kept indefinitely.
  • Native reference corrections: an item state can be unavailable; the issue call can answer 409 starter_cap_exceeded and 422 no_bundle_resolved; communication_options.channels takes EMAIL, WHATSAPP, PUSH_NOTIFICATION and SMS; errors use the {"message", "code"} body; and the guide shows GET /v1/destinations as { countries, regions }, not wrapped in data.

[5.13.0] - 2026-10-19

Added

  • valid_until on the issued package and on every package of GET /v1/esims/{iccid} (5.13.0): when the package stops working, activation plus its validity. null until the package activates.

Changed

  • expires_at is the activation deadline (5.13.0): the latest moment the package can still be activated, not when it ends. The value is unchanged; use valid_until for the end.
  • data_remaining_bytes is 0 on an ended package (5.13.0), expired or terminated. It stays null on unlimited.
  • GET /v1/esims/{iccid}/instructions answers for every eSIM GET /v1/esims/{iccid} answers for (5.13.0), also after a terminate. Language and eSIM label come from your latest booking that issued a package on the eSIM, else your defaults, else Hubby eSIM and en-US. Cache the copy for up to a week. An unsupported locale is 422.
  • {destination} accepts an id, an ISO alpha-2 or alpha-3 code, or the English name (5.13.0), in any letter case.
  • A 404 not_found answers the message Not found. (5.13.0). Branch on code.
  • GET /v1/bundles is fast (5.13.0): use it for a daily sync, revalidated with its ETag.

[5.12.0] - 2026-10-19

Added

  • A code on every refusal (5.12.0): every 4xx answers {"message", "code"}. New codes: unauthenticated, timestamp_out_of_window, not_found, item_has_no_package, method_not_allowed, malformed_query_string, validation_failed, rate_limited. Existing codes are unchanged. The reference now lists the codes each Native operation answers.
  • Idempotent-Replayed: true (5.12.0) on every replayed answer of a request sent with an Idempotency-Key.

Changed

  • A terminate spends the issue key (5.12.0): after DELETE /v1/bookings/{booking}/items/{item}/package, the Idempotency-Key that issued the package answers 409 idempotency_key_spent and issues nothing. Issue again with a new key.

[5.11.0] - 2026-10-19

Added

  • Native eSIM Integration is now server-to-server (Partner API 5.11.0): your backend books, issues the eSIM and receives the install material (LPA string, SM-DP+ address, matching id, one-tap links for iOS and Android), terminates before first use, reads status and usage, and fetches per-OS installation instructions. No traveler session and no Hubby app. Guide: Native eSIM Integration. Reference: the Native eSIM Integration tag — GET /v1/bundles, GET /v1/destinations/{destination}/bundles, POST|GET|DELETE /v1/bookings/{booking}/items/{item}/package, GET /v1/esims/{iccid}, GET /v1/esims/{iccid}/instructions.
  • Core booking operations in the reference: POST|GET /v1/bookings and GET|PATCH|DELETE /v1/bookings/{id} under the Native eSIM Integration tag, with the Booking, BookingItem and CreateBookingRequest schemas, so a client generated from the document can complete the booking-to-issue flow. The legacy /bookings operations are unchanged. Every operation under that tag now declares the HmacV1 security scheme (seconds timestamp, six-line canonical string), separate from the legacy millisecond scheme of api.hubbyesim.com.
  • prices.retail and prices.net on every bundle, and bundle_id on a package specification to book an exact product.
  • is_cancellable on every booking read and state on every booking item (queued, redeemed, installed, activated, expired, terminated, cancelled).
  • New webhook event package.terminated.
  • Those operations now live in the Partner API 5.x (core) reference. The Native eSIM Integration tag does not exist there: the operations are grouped by resource as Booking, BookingItemPackage, Esim, Destination, Bundle, CurrentPartner, RedirectToken and Analytics. The HmacV1 scheme is published there as the partnerKey, partnerSignature and partnerTimestamp security schemes.

Removed

  • The legacy /native/user-dashboard, /native/instructions, /native/device-compatibility and /native/redeem-package operations are no longer documented; the server-to-server calls above replace them.

Legacy API

[Unreleased]

Added

  • Webhook events: external_user_id on every event payload, so a receiver can route an event to the traveler without a lookup.
  • package.activated and topup.completed: package_type and duration_days — the package's type (data-limited, time-limited, unlimited, starter) and its total validity in days, so an unlimited package can be told apart and priced from the event alone. size is unchanged and keeps its meaning: for unlimited packages it is the fair-use cap the package was provisioned with (e.g. 100GB), never the string Unlimited, and it is the same value for every duration we sell. Both fields are always present, explicitly null when we cannot resolve them, and carry the same names and meanings as on package.usage.*.
  • esim_installed on the two departure-reminder events, so a partner can send "install now" to travelers who have not installed and "you're set" to those who have.
  • CSV bucket import: package_quantity column — optional column on the CSV Upload Integration; replicates the row's package specification on the booking (whole number 1–20, default 1), granting one promo code per replica. Rows with an invalid value are rejected. Multiple different package specifications per row remain unsupported.
  • booking.created webhook: packages array — one entry per granted package specification (destination, size, package_type, package_duration), not deduplicated: a booking created with package_quantity: 3 carries three identical entries, one per promo code granted. The existing destinations field stays deduplicated for backwards compatibility — use packages to count what was granted.
  • Classic eSIM grants: New per-spec esim_type: 'classic' | 'universal' field on POST /bookings, alongside a per-spec email user-key (parallel to external_user_id) and an expires_at field for bounding classic-grant redemption windows. Classic grants surface in the native app via three new Firebase callable functions (list_classic_package_queues, set_classic_package_queue_destination, claim_classic_package_queue).
  • New schema fields: package_specifications[].esim_type, package_specifications[].email, package_specifications[].expires_at. BookingResponse.package_queues[].esim_type is returned so partners can correlate which surface each queue redeems against.
  • New webhook event classic_package_queue.claimed — fires when a traveler claims a classic-eSIM grant via the native app. Opt-in via webhook_settings.events.classic_package_queue_claimed. Payload: queue_id, booking_id, esim_iccid, user_id, partner_id, is_top_up.
  • New guide: Classic eSIM Grants.

Changed

  • package.purchased now covers web app purchases: the event fires for a traveler's first purchase of a destination via the Hubby app or web app (previously native app only). Subsequent purchases for a destination the traveler already has continue to fire topup.completed, so you receive exactly one event per purchase.
  • package_specifications[].external_user_id description updated — partners can now identify the User via either external_user_id (existing) or email (new) on a per-spec basis.
  • POST /redirect-tokens/create now accepts email as an alternative to external_user_id (supply either; external_user_id wins when both are sent). The email path resolves the traveler by email scoped to your partner; the returned token is always keyed on the traveler's external_user_id (assigned automatically if the user — e.g. a classic-grant/email-only traveler — does not have one yet), so the token exchange and WebView session are unchanged.
  • departure_date is now server-defaulted to the current date when any spec is esim_type: 'classic' and the partner omits it (skeletal Booking for non-travel grants).

Removed

  • external_user_id is no longer returned at the root of the POST /bookings response. It was ambiguous (null for email-keyed bookings, only the first traveler for multi-traveler bookings). Per-traveler identity is available on the package_queues[] fan-out via each entry's uuid. Breaking for any integrator that read the root external_user_id from the booking response.

Deprecated

  • time-limited package type: The time-limited package_type is deprecated and should not be used for new integrations. To offer a fixed data allowance that expires after a set period, use a data-limited package with a short package_duration instead. Existing integrations continue to work; the type may be removed in a future API version.

[2.1.0] - 2026-03-23

Added

  • Destinations API: New endpoints for retrieving destination information
    • GET /destinations — List all available destinations (countries and regions) with localized names, ISO3 codes, and metadata
    • GET /destinations/{id} — Get a single destination by ID, including its available data bundles (packages)
  • New schemas: DestinationResponse, DestinationDetailResponse, DestinationBundleResponse

Removed

  • Countries endpoints: GET /countries, GET /countries/packages, and GET /countries/{country}/packages have been removed. Use the new Destinations API instead.

Changed

  • Native API tag renamed to Native Integration API for clarity

[1.3.2] - 2026-01-22

Added

  • Brand Support: New custom_branding field for booking requests and responses
    • Allows partners to set a specific visual style for the app experience
    • Brand identifier must correspond to a configuration that exists in the Hubby platform
    • Contact support to set up custom branding for your organization
  • Departure Location: New departure_location field for booking requests and responses
    • Capture where travelers are departing from (airport code, city name, or address)
    • Provides additional context for the booking

[1.3.1] - 2025-10-27

Added

  • Promo Code Claiming: New feature to share claim links instead of promo codes directly
    • Added uuid and claim_url fields to promo code responses
    • New public endpoint POST /api/promo-codes/claim/{uuid} for claiming promo codes with email capture
    • Claim URLs are only included when bookings don't have an email address
    • Immediate email delivery of promo codes upon claiming
    • Perfect for events, marketing campaigns, and lead capture
  • Promo Code Retrieval: New endpoint GET /api/promo-codes/{id} for retrieving promo code details
    • Partners can fetch their promo codes with full metadata
    • Includes RBAC authorization for organization-level access control
  • Enhanced Promo Code Documentation: Added comprehensive guide on promo code claiming feature

Changed

  • Promo code response schema now includes additional metadata fields: iso3, package_type, package_duration

[1.3.0] - 2025-07-27

Added

  • Package Type Support: Added support for multiple package types in booking requests
    • package_type parameter with options: starter, data-limited, unlimited, time-limited
    • package_duration parameter to specify package duration in days
    • Default values: data-limited for package type, 365 days for data-limited packages, 2 days for starter and time-limited packages
  • Enhanced Package Documentation: Added comprehensive documentation explaining the differences between package types
    • Starter packages: Small data allowance for short duration, ideal for trials (default: 2 days)
    • Data-limited packages: Traditional packages with specific data allowance (default: 365 days)
    • Unlimited packages: Unrestricted data usage with fair use policy
    • Time-limited packages: Fixed data allowance with time constraint for full-speed access
    • Clarification that top-ups are always data-limited packages

Changed

  • API Version: Bumped the version to v1.3.0
  • Default Package Type: Data-limited packages are now the primary focus and default option

[1.2.0] - 2025-05-12

Added

  • Endpoints added for in app integration The first endpoints for in-app integration have been added.
  • External ID Support: Added optional external_id property to bookings for better integration with external systems.

Changed

  • Destinationless bookings A booking no longer requires a destination to be specified, the destination can now be picked by the end user.
  • Bumped the version to v1.2.0

[1.1.0] - 2025-03-16

Added

  • HMAC Auth: HMAC Authentication is now the default for this API
  • Regen promo codes: Added the option to regenerate codes.

Changed

  • Enhanced Entity Details: All models now includes additional metadata fields, such as created_by and updated_at, to provide better context for each..

Fixed

  • Time Format Consistency: Standardized all time-related fields to use ISO 8601 format, ensuring consistency across the API.

[1.0.0] - 2023-02-15

Initial Release

  • Launched the Hubby eSIM API with core features including creating a booking and fetching packages.