Hubby Partner API (5.20.0)

Every response of a matched route carries the header X-Hubby-Call-Id, the id of the call in Hubby's records. A request refused before it reaches a route carries no call id: an unknown path (404, code not_found), a method the path does not take (405, code method_not_allowed), or a query string PHP cannot parse whole (400, code malformed_query_string). Those two refusals, 405 and 400, can answer any path and are not listed per operation. Every 4xx refusal body carries a message and a code to branch on. A 5xx can carry a message alone: the issue call's 500 (POST /v1/bookings/{booking}/items/{item}/package) has no code; retry it with the same Idempotency-Key.

Download OpenAPI description
Languages
Servers
Production
https://core.hubbyesim.com/api/
Staging
https://core-staging.hubbyesim.com/api/

Analytics

Operations

Booking

Operations

Create a booking for the authenticated partner

Request

A package specification is a request, not a catalog selection: an accepted size or package_duration that no bundle carries exactly is fulfilled at redemption by the closest bundle of the same package type at the same destination. Only a specification no amount of closeness can satisfy is refused — naming a package_type the destination does not sell at all answers 422 with a package_specifications.{index}.package_type error.

Headers
Idempotency-Keystring

Optional. A unique key for this request, for example a UUID, reused across every retry of it. A retry with the same key replays the stored success for 24 hours instead of running the request again.

Bodyapplication/jsonrequired
external_referencestring<= 255 charactersrequired

Your own reference for the booking. It is unique per Partner and depart_at among your live bookings (a cancelled booking and a booking brought over from the previous backend do not count). Sending the same external_reference and depart_at again returns the existing booking with 201 and writes nothing, so a retry is safe. After you cancel that booking, the same external_reference and depart_at create a new booking. Bookings are never merged: the same external_reference with another depart_at creates a second booking. A lookup by reference (GET /v1/bookings/{id}) answers the newest booking that carries it; filter the list by external_reference to read all of them.

external_reference_secondarystring or null<= 255 characters
visual_identity_idstring or null
depart_atstring or null

The trip's departure day, written DD/MM/YYYY, DD-MM-YYYY or YYYY-MM-DD (day first; an ISO 8601 date-time such as 2026-08-01T10:00:00Z is accepted too). Answers return it as a date-time. Part of the retry key: see external_reference.

return_atstring or null

The trip's return day, written DD/MM/YYYY, DD-MM-YYYY or YYYY-MM-DD (day first; an ISO 8601 date-time is accepted too), on or after depart_at.

localestring or null<= 10 characters

The booking's language as a BCP-47 tag. es, es-ES and es_es are all accepted and stored as one normalised tag. Up to 10 characters: a longer value is refused with 422. A value Hubby cannot read as a language is stored as null with no error, and your default_locale (GET /v1/me) then fills it.

emailstring or null(email)<= 255 characters
phonestring or null<= 50 characters
first_namestring or null<= 255 characters
last_namestring or null<= 255 characters
full_namestring or null<= 255 characters
titlestring or null<= 20 characters
genderstring or null<= 20 characters
date_of_birthstring or null(date)

The traveller's date of birth, a day before today, written YYYY-MM-DD. Answers return it as YYYY-MM-DD.

paxinteger or null[ 1 .. 2147483647 ]

How many travellers the booking is for. A headcount only: it creates no packages. Send one package specification per traveller.

flight_numberstring or null<= 20 characters
departure_locationstring or null<= 255 characters
dataobject or null

Your own data about the booking: any JSON object of up to 100 keys, with any values. Stored, never returned: no booking read answers it.

communication_optionsobject or null

Your messaging preferences for the traveller. Hubby stores and returns it; it does not control Hubby's messages. should_send_message is required when the object is sent; channels is optional.

package_specificationsArray of objectsnon-emptyrequired

The package specifications to fulfil, one per traveller. When GET /v1/me reports a package_strategy, package_type may be omitted and size and package_duration are not required by the type: the strategy overwrites all three on every specification you create, whether or not you send them. Each specification must carry email or external_user_id when GET /v1/me reports end_customer_channel: webapp. With end_customer_channel: native_app, both are optional and a specification naming neither is claimed by its code. With end_customer_channel: partner_app, booking items carry no code, so a specification must name its traveller by external_user_id before you issue it: send it here or on the issue call; for an item that names no traveller, the issue call answers 422 on external_user_id.

package_specifications[].​external_user_idstring or null<= 255 characters
package_specifications[].​emailstring or null(email)<= 255 characters
package_specifications[].​destinationstring<= 120 characters

Required unless bundle_id is sent. The country or region the package is for: an ISO 3166-1 alpha-2 or alpha-3 code, a region's code, or the destination's English name, in any letter case. The value must match exactly: a translated name, a typo or any other value answers 422, as does a destination outside your destination list. A specification with neither destination nor bundle_id is refused here, at booking: 422 validation_failed on this field, and nothing is booked. Only when GET /v1/me reports force_destinationless: true (never on a Partner App account) may it be omitted: a sent one is then ignored, the item is booked without a destination, and issuing it over this API answers 422 destination_required.

package_specifications[].​package_typestring

Required unless bundle_id is sent. data-limited (or data_limited), starter or unlimited. Both spellings are accepted; answers always spell it data_limited. May also be omitted when GET /v1/me reports a package_strategy, which sets it for you.

Enum"data_limited""data-limited""starter""unlimited"
package_specifications[].​sizestring or null^\d+(\.\d+)?(MB|GB)$

The data allowance, as a number followed by MB or GB (500MB, 1.5GB). Required for data_limited and starter, unless bundle_id is sent or GET /v1/me reports a package_strategy.

package_specifications[].​package_durationinteger or null>= 1

The validity in whole days. Required for unlimited and starter, unless bundle_id is sent or GET /v1/me reports a package_strategy.

package_specifications[].​start_atstring or null

The day the traveller plans to start using this package, written DD/MM/YYYY, DD-MM-YYYY or YYYY-MM-DD (day first; an ISO 8601 date-time is accepted too). Stored and returned on the item; it does not start the package, which starts when it is activated.

package_specifications[].​end_atstring or null

The day the traveller plans to stop using this package, written DD/MM/YYYY, DD-MM-YYYY or YYYY-MM-DD (day first; an ISO 8601 date-time is accepted too), after start_at. Stored and returned on the item; it does not end the package. After this day the item's upgrade offer is no longer shown.

package_specifications[].​bundle_idstring or null

A bundle id from the catalogue (GET /v1/bundles). Books exactly that bundle: its destination, type, size and duration are read from it, so destination, package_type, size and package_duration may be omitted and are ignored when sent. A bundle outside your catalogue answers 422. Omit it to specify by dimensions instead: the item then keeps the size you asked for, and Hubby picks the closest bundle of the same type when the item is issued. Refused with 422 when GET /v1/me reports a package_strategy, or when your bookings are made without a destination: those settings choose the bundle for you.

partnerstring or null<= 255 characters

Create this booking for one of your descendant partners instead of your own account, naming it by its slug (the value GET /v1/me reports). Optional: omit it and the booking is yours, exactly as before. No setting on your API key is needed — every account may do this. The named partner must be an active partner below you in the hierarchy, at any depth rather than only a direct child, and every partner between you and it must be active too. Naming anything else — a partner that is not yours, an inactive one, or a slug that does not exist — answers 422. The whole booking is then the named partner's: its catalog and price list, its visual identities, its Package strategy, and its customers. Only the named partner's own key can read, change, cancel or issue it, and its webhooks go to that partner. Your own key answers 404 for it from then on. Key inheritance covers analytics only: no setting lets a parent read, change, cancel or issue a descendant's booking, or receive its webhooks.

curl -i -X POST \
  https://core.hubbyesim.com/api/v1/bookings \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: string' \
  -H 'X-Api-Key: YOUR_API_KEY_HERE' \
  -H 'X-Signature: YOUR_API_KEY_HERE' \
  -H 'X-Timestamp: YOUR_API_KEY_HERE' \
  -d '{
    "external_reference": "string",
    "external_reference_secondary": "string",
    "visual_identity_id": "string",
    "depart_at": "string",
    "return_at": "string",
    "locale": "string",
    "email": "user@example.com",
    "phone": "string",
    "first_name": "string",
    "last_name": "string",
    "full_name": "string",
    "title": "string",
    "gender": "string",
    "date_of_birth": "2019-08-24",
    "pax": 1,
    "flight_number": "string",
    "departure_location": "string",
    "data": {
      "property1": null,
      "property2": null
    },
    "communication_options": {
      "should_send_message": true,
      "channels": [
        "EMAIL"
      ]
    },
    "package_specifications": [
      {
        "external_user_id": "string",
        "email": "user@example.com",
        "destination": "string",
        "package_type": "data_limited",
        "size": "string",
        "package_duration": 1,
        "start_at": "string",
        "end_at": "string",
        "bundle_id": "string"
      }
    ],
    "partner": "string"
  }'

Responses

Booking

Headers
Idempotent-Replayedstring

Sent as true only when this answer is the stored answer to an earlier request with the same Idempotency-Key, replayed unchanged. Absent on the first answer.

X-Hubby-Call-Idstringrequired

The id of this call in Hubby's records. Quote it when you ask Hubby about a call.

Bodyapplication/json
dataobject(Booking)required
data.​idstringrequired
data.​partner_idstringrequired
data.​visual_identity_idstring or nullrequired
data.​external_referencestring or nullrequired
data.​external_reference_secondarystring or nullrequired
data.​sourcestring(BookingSource)required
Enum"api""platform""manual""synthetic""migrated""free_esim""import""membership""store"
data.​depart_atstring or null(date-time)required
data.​return_atstring or null(date-time)required
data.​localestring or nullrequired
data.​emailstring or nullrequired
data.​phonestring or nullrequired
data.​first_namestring or nullrequired
data.​last_namestring or nullrequired
data.​full_namestring or nullrequired
data.​titlestring or nullrequired
data.​genderstring or nullrequired
data.​date_of_birthstring or nullrequired
data.​paxinteger or nullrequired
data.​flight_numberstring or nullrequired
data.​departure_locationstring or nullrequired
data.​communication_optionsobject or nullrequired

The communication_options you sent. Hubby stores and returns it; it does not control Hubby's messages. Hubby emails a traveller only when Hubby has turned on emails for your account, by agreement.

data.​communication_options.​should_send_messageboolean
data.​communication_options.​channelsArray of strings
data.​cancelled_atstring or null(date-time)required
data.​is_cancellablebooleanrequired

Whether DELETE /v1/bookings/{id} would cancel this booking now. False once it is cancelled, after depart_at, while any item is issued, or while a traveller's money sits on an item. Read it before offering a cancel button.

data.​is_pseudonymizedbooleanrequired
data.​itemsArray of objects(BookingItem)required

The booking's items, one per package specification. Each item's id is the {item} you issue, read and terminate its package with.

data.​items[].​idstringrequired
data.​items[].​codestring or null

The human-typeable redemption code for this package, twelve characters from an unambiguous alphabet. It is a bearer credential: whoever redeems it is bound to the package, so treat it like a voucher and do not put it anywhere public. Absent when your End-customer channel is partner_app.

data.​items[].​offer_uuidstring or null

The key of this item's upgrade-offer page, where the traveller can buy a larger package before redeeming. Distinct from id: id is what the outbound webhooks send as package_queue_uuid, this is the page's own token, and the two are never interchangeable. Use it only if you build the link yourself — upgrade_url is already the whole link. Absent when your End-customer channel is partner_app.

data.​items[].​upgrade_urlstring or null

The traveller's upgrade-offer link for this item, ready to send. It is a bearer capability URL — whoever opens it can buy an upgrade for this package — so treat it like code and keep it out of anywhere public. Null only on an environment with no public-pages deployment configured. Absent when your End-customer channel is partner_app.

data.​items[].​deep_link_urlstring or null

A universal link that opens the Hubby app and applies this item's code automatically, so a traveller never types it. On a device without the app it leads to the store instead; on desktop, to a download page — it is a mobile CTA, not a general redemption URL. Send it as given rather than rebuilding it. Null for an item carrying no code. Absent when your End-customer channel is partner_app.

data.​items[].​external_user_idstring or null
data.​items[].​emailstring or null
data.​items[].​customer_idstring or nullrequired
data.​items[].​destinationstring or null

The country's ISO 3166-1 alpha-2 code, or the region's code (for example EU). List the region codes from GET /v1/destinations.

data.​items[].​package_typePackageType (string) or nullrequired
Any of:
string(PackageType)
Enum"data_limited""starter""unlimited"
data.​items[].​size_bytesinteger or nullrequired

The size you asked for, in bytes: the size of the specification, or the bundle's size when you named a bundle_id. Not always the size issued: Hubby picks the closest bundle of the same type at issue, so read the issued size from the bundle package.bundle_id names. Null for a specification with no size.

data.​items[].​duration_secondsinteger or nullrequired
data.​items[].​start_atstring or null(date-time)required
data.​items[].​end_atstring or null(date-time)required
data.​items[].​statusstring(PackageQueueStatus)required

Deprecated: read state instead. state covers the whole lifecycle of the item, including the package issued from it.

Enum"queued""pending_payment""paid""activated""cancelled""reverted""unresolved"
data.​items[].​bundle_idstring or nullrequired

The bundle this item books: the one named by bundle_id at booking, or, once issued, the bundle the closest match chose. Null while an item specified by dimensions is not issued yet.

data.​items[].​statestring(PackageLifecycleState)required

Where this item is in its lifecycle. Read it instead of status: queued (reserved, not issued yet; you can issue it), unavailable (cannot be issued now: no bundle answers its specification, or a payment is still pending; cancel the booking or book again), redeemed (issued, install material available; with package: null, already spent, or brought over spent from the previous backend, so there is nothing to install), installed (the eSIM is on a phone), activated (live on the network, validity running), expired (the validity elapsed, or the provider ended the package after it activated; a package that used up its data stays activated until the provider ends it), terminated (ended before activation; on an item this is rare: when a package you issued ends before activation, the item returns to queued with package: null, so an item reads terminated only when the package showed data use before Hubby recorded its activation, or when the traveller redeemed the item in the Hubby app; treat it like expired), cancelled (the booking was cancelled, or the item's payment was rolled back).

Enum"queued""unavailable""redeemed""installed""activated""expired""terminated""cancelled"
data.​items[].​packageIssuedPackage (object) or nullrequired

The package issued for this item, the same body GET …/items/{item}/package answers. Null until it is issued.

Any of:

The package issued for this item, the same body GET …/items/{item}/package answers. Null until it is issued.

data.​items[].​package.​idstringrequired
data.​items[].​package.​statestring(PackageLifecycleState)required

Where this package is in its lifecycle: redeemed (issued, install material available), installed (the eSIM is on a phone), activated (live on the network, validity running), expired (the validity elapsed, or the provider ended the package after it activated; a package that used up its data stays activated until the provider ends it), terminated (ended before activation).

Enum"queued""unavailable""redeemed""installed""activated""expired""terminated""cancelled"
data.​items[].​package.​bundle_idstringrequired

The bundle this package was issued from.

data.​items[].​package.​issued_atstring or null(date-time)required

When the package was issued.

data.​items[].​package.​installed_atstring or null(date-time)required

When this package became installable on a phone: the eSIM's install, or the issue itself when the eSIM was already installed. Null until the eSIM is on a phone.

data.​items[].​package.​activated_atstring or null(date-time)required

When the package activated and its validity started: the time the connectivity provider reported the package activated. Hubby does not observe the network attach itself. Null until then.

data.​items[].​package.​expires_atstring or null(date-time)required

The latest moment this package can still be activated: the deadline the connectivity provider sets when the package is issued, after which an unused package can no longer be started. It is not when the package's validity ends: a package with a few days of validity is typically still activatable for about a year. Use valid_until for when the package stops working.

data.​items[].​package.​valid_untilstring or null(date-time)required

When this package's validity ends and its data stops working: the moment it activated plus its time allowance. Null until the package activates, because the clock starts at activation, not at issue. Also null for a package that carries no time allowance.

data.​items[].​package.​terminated_atstring or null(date-time)required

When the package ended: before activation (state is terminated), or after activation when the provider ended it (state is expired). Null while the package has not ended.

data.​items[].​package.​esimobject(EsimInstallMaterial)required
data.​items[].​package.​esim.​iccidstringrequired
data.​items[].​package.​esim.​installed_atstring or null(date-time)required

When this eSIM was first installed on a phone, whichever package it was installed for. Set for a repeat traveller: the eSIM is already on their phone, so skip the install screen and show that the new package is ready. Null until the eSIM is installed.

data.​items[].​package.​esim.​lpastring or nullrequired

The activation string. Render it as a QR code for "install on another device".

data.​items[].​package.​esim.​smdp_addressstring or nullrequired

The SM-DP+ address from lpa, for manual entry in the phone's settings.

data.​items[].​package.​esim.​matching_idstring or nullrequired

The matching id (activation code) from lpa, for manual entry in the phone's settings.

data.​items[].​package.​esim.​install_linksobjectrequired

One-tap install links. Open the one for the phone that will hold the eSIM; its own installer opens. Each is null when the activation string is not in the standard LPA:1$… form.

data.​created_atstring or null(date-time)required
data.​updated_atstring or null(date-time)required
Response
application/json
{ "data": { "id": "string", "partner_id": "string", "visual_identity_id": "string", "external_reference": "string", "external_reference_secondary": "string", "source": "api", "depart_at": "2019-08-24T14:15:22Z", "return_at": "2019-08-24T14:15:22Z", "locale": "string", "email": "string", "phone": "string", "first_name": "string", "last_name": "string", "full_name": "string", "title": "string", "gender": "string", "date_of_birth": "string", "pax": 0, "flight_number": "string", "departure_location": "string", "communication_options": { … }, "cancelled_at": "2019-08-24T14:15:22Z", "is_cancellable": true, "is_pseudonymized": true, "items": [ … ], "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z" } }

List your bookings, one cursor page at a time

Request

With format=csv, the response is every booking that departs from departure_from to departure_to (both then required), in departure order, up to 5000 bookings, and not one page. The response is JSON in both cases. pagination.hasNextPage is then true when more than 5000 bookings matched: send a shorter date range.

Query
per_pageinteger[ 1 .. 100 ]

How many bookings one page holds, 1 to 100.

Default 10
cursorstring or null

The page to read: a pagination.nextCursor or pagination.prevCursor from the previous page, sent back unchanged. Omit it for the first page.

emailstring or null(email)<= 255 characters

Only bookings whose email is this address; letter case does not matter.

external_referencestring or null<= 255 characters

Only bookings that carry exactly this external_reference; every booking that carries it is listed, not only the newest.

depart_atstring or null(date)

Only bookings that depart on this day, YYYY-MM-DD. Matches the whole day in UTC, not an instant: a time sent with the day is ignored.

created_atstring or null(date)

Only bookings created on this day, YYYY-MM-DD. Matches the whole day in UTC, not an instant: a time sent with the day is ignored.

promo_codestring or null<= 255 characters

Only bookings with an item whose code is this value.

destinationstring or null<= 120 characters

Only bookings with an item for this destination: an ISO 3166-1 alpha-2 or alpha-3 code, a region code or a destination name, matched on any item of the booking. With this filter every matching booking comes in one answer, sorted as asked, and pagination carries no cursors; per_page and cursor are ignored.

sortstring

The column to sort by.

Default "created_at"
Enum"created_at""depart_at""external_reference"
orderstring

The sort direction.

Default "desc"
Enum"asc""desc"
formatstring or null

csv switches to the export answer: every booking that departs from departure_from to departure_to, both then required, in departure order, up to 5000 bookings in one JSON answer. No CSV file is produced; the body has the same shape as a page, with pagination.hasNextPage only.

Enum"csv"null
departure_fromstring or null(date)

With format=csv: the first departure day to include, YYYY-MM-DD. Required then, ignored otherwise.

departure_tostring or null(date)

With format=csv: the last departure day to include, YYYY-MM-DD, on or after departure_from. Required then, ignored otherwise.

curl -i -X GET \
  'https://core.hubbyesim.com/api/v1/bookings?created_at=2019-08-24&cursor=string&depart_at=2019-08-24&departure_from=2019-08-24&departure_to=2019-08-24&destination=string&email=user%40example.com&external_reference=string&format=csv&order=asc&per_page=10&promo_code=string&sort=created_at' \
  -H 'X-Api-Key: YOUR_API_KEY_HERE' \
  -H 'X-Signature: YOUR_API_KEY_HERE' \
  -H 'X-Timestamp: YOUR_API_KEY_HERE'

Responses

Headers
X-Hubby-Call-Idstringrequired

The id of this call in Hubby's records. Quote it when you ask Hubby about a call.

Bodyapplication/json
Any of:
dataArray of objects(Booking)required
data[].​idstringrequired
data[].​partner_idstringrequired
data[].​visual_identity_idstring or nullrequired
data[].​external_referencestring or nullrequired
data[].​external_reference_secondarystring or nullrequired
data[].​sourcestring(BookingSource)required
Enum"api""platform""manual""synthetic""migrated""free_esim""import""membership""store"
data[].​depart_atstring or null(date-time)required
data[].​return_atstring or null(date-time)required
data[].​localestring or nullrequired
data[].​emailstring or nullrequired
data[].​phonestring or nullrequired
data[].​first_namestring or nullrequired
data[].​last_namestring or nullrequired
data[].​full_namestring or nullrequired
data[].​titlestring or nullrequired
data[].​genderstring or nullrequired
data[].​date_of_birthstring or nullrequired
data[].​paxinteger or nullrequired
data[].​flight_numberstring or nullrequired
data[].​departure_locationstring or nullrequired
data[].​communication_optionsobject or nullrequired

The communication_options you sent. Hubby stores and returns it; it does not control Hubby's messages. Hubby emails a traveller only when Hubby has turned on emails for your account, by agreement.

data[].​communication_options.​should_send_messageboolean
data[].​communication_options.​channelsArray of strings
data[].​cancelled_atstring or null(date-time)required
data[].​is_cancellablebooleanrequired

Whether DELETE /v1/bookings/{id} would cancel this booking now. False once it is cancelled, after depart_at, while any item is issued, or while a traveller's money sits on an item. Read it before offering a cancel button.

data[].​is_pseudonymizedbooleanrequired
data[].​itemsArray of objects(BookingItem)required

The booking's items, one per package specification. Each item's id is the {item} you issue, read and terminate its package with.

data[].​items[].​idstringrequired
data[].​items[].​codestring or null

The human-typeable redemption code for this package, twelve characters from an unambiguous alphabet. It is a bearer credential: whoever redeems it is bound to the package, so treat it like a voucher and do not put it anywhere public. Absent when your End-customer channel is partner_app.

data[].​items[].​offer_uuidstring or null

The key of this item's upgrade-offer page, where the traveller can buy a larger package before redeeming. Distinct from id: id is what the outbound webhooks send as package_queue_uuid, this is the page's own token, and the two are never interchangeable. Use it only if you build the link yourself — upgrade_url is already the whole link. Absent when your End-customer channel is partner_app.

data[].​items[].​upgrade_urlstring or null

The traveller's upgrade-offer link for this item, ready to send. It is a bearer capability URL — whoever opens it can buy an upgrade for this package — so treat it like code and keep it out of anywhere public. Null only on an environment with no public-pages deployment configured. Absent when your End-customer channel is partner_app.

data[].​items[].​deep_link_urlstring or null

A universal link that opens the Hubby app and applies this item's code automatically, so a traveller never types it. On a device without the app it leads to the store instead; on desktop, to a download page — it is a mobile CTA, not a general redemption URL. Send it as given rather than rebuilding it. Null for an item carrying no code. Absent when your End-customer channel is partner_app.

data[].​items[].​external_user_idstring or null
data[].​items[].​emailstring or null
data[].​items[].​customer_idstring or nullrequired
data[].​items[].​destinationstring or null

The country's ISO 3166-1 alpha-2 code, or the region's code (for example EU). List the region codes from GET /v1/destinations.

data[].​items[].​package_typePackageType (string) or nullrequired
Any of:
string(PackageType)
Enum"data_limited""starter""unlimited"
data[].​items[].​size_bytesinteger or nullrequired

The size you asked for, in bytes: the size of the specification, or the bundle's size when you named a bundle_id. Not always the size issued: Hubby picks the closest bundle of the same type at issue, so read the issued size from the bundle package.bundle_id names. Null for a specification with no size.

data[].​items[].​duration_secondsinteger or nullrequired
data[].​items[].​start_atstring or null(date-time)required
data[].​items[].​end_atstring or null(date-time)required
data[].​items[].​statusstring(PackageQueueStatus)required

Deprecated: read state instead. state covers the whole lifecycle of the item, including the package issued from it.

Enum"queued""pending_payment""paid""activated""cancelled""reverted""unresolved"
data[].​items[].​bundle_idstring or nullrequired

The bundle this item books: the one named by bundle_id at booking, or, once issued, the bundle the closest match chose. Null while an item specified by dimensions is not issued yet.

data[].​items[].​statestring(PackageLifecycleState)required

Where this item is in its lifecycle. Read it instead of status: queued (reserved, not issued yet; you can issue it), unavailable (cannot be issued now: no bundle answers its specification, or a payment is still pending; cancel the booking or book again), redeemed (issued, install material available; with package: null, already spent, or brought over spent from the previous backend, so there is nothing to install), installed (the eSIM is on a phone), activated (live on the network, validity running), expired (the validity elapsed, or the provider ended the package after it activated; a package that used up its data stays activated until the provider ends it), terminated (ended before activation; on an item this is rare: when a package you issued ends before activation, the item returns to queued with package: null, so an item reads terminated only when the package showed data use before Hubby recorded its activation, or when the traveller redeemed the item in the Hubby app; treat it like expired), cancelled (the booking was cancelled, or the item's payment was rolled back).

Enum"queued""unavailable""redeemed""installed""activated""expired""terminated""cancelled"
data[].​items[].​packageIssuedPackage (object) or nullrequired

The package issued for this item, the same body GET …/items/{item}/package answers. Null until it is issued.

Any of:

The package issued for this item, the same body GET …/items/{item}/package answers. Null until it is issued.

data[].​items[].​package.​idstringrequired
data[].​items[].​package.​statestring(PackageLifecycleState)required

Where this package is in its lifecycle: redeemed (issued, install material available), installed (the eSIM is on a phone), activated (live on the network, validity running), expired (the validity elapsed, or the provider ended the package after it activated; a package that used up its data stays activated until the provider ends it), terminated (ended before activation).

Enum"queued""unavailable""redeemed""installed""activated""expired""terminated""cancelled"
data[].​items[].​package.​bundle_idstringrequired

The bundle this package was issued from.

data[].​items[].​package.​issued_atstring or null(date-time)required

When the package was issued.

data[].​items[].​package.​installed_atstring or null(date-time)required

When this package became installable on a phone: the eSIM's install, or the issue itself when the eSIM was already installed. Null until the eSIM is on a phone.

data[].​items[].​package.​activated_atstring or null(date-time)required

When the package activated and its validity started: the time the connectivity provider reported the package activated. Hubby does not observe the network attach itself. Null until then.

data[].​items[].​package.​expires_atstring or null(date-time)required

The latest moment this package can still be activated: the deadline the connectivity provider sets when the package is issued, after which an unused package can no longer be started. It is not when the package's validity ends: a package with a few days of validity is typically still activatable for about a year. Use valid_until for when the package stops working.

data[].​items[].​package.​valid_untilstring or null(date-time)required

When this package's validity ends and its data stops working: the moment it activated plus its time allowance. Null until the package activates, because the clock starts at activation, not at issue. Also null for a package that carries no time allowance.

data[].​items[].​package.​terminated_atstring or null(date-time)required

When the package ended: before activation (state is terminated), or after activation when the provider ended it (state is expired). Null while the package has not ended.

data[].​items[].​package.​esimobject(EsimInstallMaterial)required
data[].​items[].​package.​esim.​iccidstringrequired
data[].​items[].​package.​esim.​installed_atstring or null(date-time)required

When this eSIM was first installed on a phone, whichever package it was installed for. Set for a repeat traveller: the eSIM is already on their phone, so skip the install screen and show that the new package is ready. Null until the eSIM is installed.

data[].​items[].​package.​esim.​lpastring or nullrequired

The activation string. Render it as a QR code for "install on another device".

data[].​items[].​package.​esim.​smdp_addressstring or nullrequired

The SM-DP+ address from lpa, for manual entry in the phone's settings.

data[].​items[].​package.​esim.​matching_idstring or nullrequired

The matching id (activation code) from lpa, for manual entry in the phone's settings.

data[].​items[].​package.​esim.​install_linksobjectrequired

One-tap install links. Open the one for the phone that will hold the eSIM; its own installer opens. Each is null when the activation string is not in the standard LPA:1$… form.

data[].​created_atstring or null(date-time)required
data[].​updated_atstring or null(date-time)required
paginationobjectrequired
pagination.​perPageintegerrequired
pagination.​nextCursorstring or nullrequired
pagination.​prevCursorstring or nullrequired
pagination.​hasNextPagebooleanrequired
pagination.​hasPrevPagebooleanrequired
Response
application/json
{ "data": [ { … } ], "pagination": { "hasNextPage": true } }

Get a presigned URL to upload a booking dataset file

Request

PUT the CSV to upload_url with every header in required_headers, before expires_at: the URL is valid for 15 minutes. bucket and object_path name where the file lands, under your own account and the dataset and date you sent. Hubby reads the file and creates its bookings.

Headers
Idempotency-Keystring

Optional. A unique key for this request, for example a UUID, reused across every retry of it. A retry with the same key replays the stored success for 24 hours instead of running the request again.

Bodyapplication/jsonrequired
datestring(date)required

The day the file is for, Y-m-d. One file per dataset and day.

datasetstring^[A-Za-z0-9_-]{1,64}$

The dataset the file belongs to: letters, digits, _ and -, up to 64 characters. Default bookings.

overwriteboolean

Replace the file already uploaded for this dataset and day. Default false: an existing file is then refused with 409 upload_already_exists.

curl -i -X POST \
  https://core.hubbyesim.com/api/v1/bookings/imports/presign-upload \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: string' \
  -H 'X-Api-Key: YOUR_API_KEY_HERE' \
  -H 'X-Signature: YOUR_API_KEY_HERE' \
  -H 'X-Timestamp: YOUR_API_KEY_HERE' \
  -d '{
    "date": "2019-08-24",
    "dataset": "string",
    "overwrite": true
  }'

Responses

Headers
Idempotent-Replayedstring

Sent as true only when this answer is the stored answer to an earlier request with the same Idempotency-Key, replayed unchanged. Absent on the first answer.

X-Hubby-Call-Idstringrequired

The id of this call in Hubby's records. Quote it when you ask Hubby about a call.

Bodyapplication/json
bucketstringrequired
object_pathstringrequired
upload_urlstringrequired
expires_atstringrequired
required_headersobjectrequired
required_headers.​property name*stringadditional property
Response
application/json
{ "bucket": "string", "object_path": "string", "upload_url": "string", "expires_at": "string", "required_headers": { "property1": "string", "property2": "string" } }

Change the package specification of every item of a booking

Request

Items whose package is already issued are skipped and counted in skipped_count, which is 0 when every item changed. {bookingId} accepts the same identifiers as GET /v1/bookings/{id}. To change one item only, send the same body to PATCH /v1/bookings/{bookingId}/package-specifications/{uuid}.

Path
bookingIdstringrequired
Headers
Idempotency-Keystring

Optional. A unique key for this request, for example a UUID, reused across every retry of it. A retry with the same key replays the stored success for 24 hours instead of running the request again.

Bodyapplication/json
destinationstring or null<= 120 characters

The country or region the item is for: an ISO 3166-1 alpha-2 or alpha-3 code, a region's code, or the destination's English name, in any letter case. The value must match exactly: a translated name, a typo or any other value answers 422.

package_typestring or null

data-limited (or data_limited), starter or unlimited. Both spellings are accepted; answers always spell it data_limited.

Enum"data_limited""data-limited""starter""unlimited"null
sizestring or null^\d+(\.\d+)?(MB|GB)$
package_durationinteger or null>= 1
bundle_idstring or null

A bundle id from the catalogue (GET /v1/bundles). The item takes that bundle's destination, type, size and duration, and is provisioned with exactly that bundle; destination, package_type, size and package_duration are ignored when sent with it. A bundle outside your catalogue answers 422. Omit it to re-specify by dimensions, which lets go of any bundle named before. Refused with 422 when GET /v1/me reports a package_strategy, or when your bookings are made without a destination: those settings choose the bundle for you. Also refused with 422 on an item Hubby support granted, whose bundle support chose.

curl -i -X PATCH \
  'https://core.hubbyesim.com/api/v1/bookings/{bookingId}/package-specifications' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: string' \
  -H 'X-Api-Key: YOUR_API_KEY_HERE' \
  -H 'X-Signature: YOUR_API_KEY_HERE' \
  -H 'X-Timestamp: YOUR_API_KEY_HERE' \
  -d '{
    "destination": "string",
    "package_type": "data_limited",
    "size": "string",
    "package_duration": 1,
    "bundle_id": "string"
  }'

Responses

Headers
Idempotent-Replayedstring

Sent as true only when this answer is the stored answer to an earlier request with the same Idempotency-Key, replayed unchanged. Absent on the first answer.

X-Hubby-Call-Idstringrequired

The id of this call in Hubby's records. Quote it when you ask Hubby about a call.

Bodyapplication/json
updated_countintegerrequired
skipped_countintegerrequired
Response
application/json
{ "updated_count": 0, "skipped_count": 0 }

Change the package specification of one booking item

Request

An item whose package is already issued is refused with 400, so skipped_count is always 0 here. {bookingId} accepts the same identifiers as GET /v1/bookings/{id}.

Path
bookingIdstringrequired
uuidstringrequired

The item to change: its id in the booking's items (items[].id), not its offer_uuid.

Headers
Idempotency-Keystring

Optional. A unique key for this request, for example a UUID, reused across every retry of it. A retry with the same key replays the stored success for 24 hours instead of running the request again.

Bodyapplication/json
destinationstring or null<= 120 characters

The country or region the item is for: an ISO 3166-1 alpha-2 or alpha-3 code, a region's code, or the destination's English name, in any letter case. The value must match exactly: a translated name, a typo or any other value answers 422.

package_typestring or null

data-limited (or data_limited), starter or unlimited. Both spellings are accepted; answers always spell it data_limited.

Enum"data_limited""data-limited""starter""unlimited"null
sizestring or null^\d+(\.\d+)?(MB|GB)$
package_durationinteger or null>= 1
bundle_idstring or null

A bundle id from the catalogue (GET /v1/bundles). The item takes that bundle's destination, type, size and duration, and is provisioned with exactly that bundle; destination, package_type, size and package_duration are ignored when sent with it. A bundle outside your catalogue answers 422. Omit it to re-specify by dimensions, which lets go of any bundle named before. Refused with 422 when GET /v1/me reports a package_strategy, or when your bookings are made without a destination: those settings choose the bundle for you. Also refused with 422 on an item Hubby support granted, whose bundle support chose.

curl -i -X PATCH \
  'https://core.hubbyesim.com/api/v1/bookings/{bookingId}/package-specifications/{uuid}' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: string' \
  -H 'X-Api-Key: YOUR_API_KEY_HERE' \
  -H 'X-Signature: YOUR_API_KEY_HERE' \
  -H 'X-Timestamp: YOUR_API_KEY_HERE' \
  -d '{
    "destination": "string",
    "package_type": "data_limited",
    "size": "string",
    "package_duration": 1,
    "bundle_id": "string"
  }'

Responses

Headers
Idempotent-Replayedstring

Sent as true only when this answer is the stored answer to an earlier request with the same Idempotency-Key, replayed unchanged. Absent on the first answer.

X-Hubby-Call-Idstringrequired

The id of this call in Hubby's records. Quote it when you ask Hubby about a call.

Bodyapplication/json
updated_countintegerrequired
Value 1
skipped_countintegerrequired
Response
application/json
{ "updated_count": 1, "skipped_count": 0 }

Read one booking

Request

{id} is the booking's id, its id in the legacy API, or your own external_reference, tried in that order.

Path
idstringrequired
curl -i -X GET \
  'https://core.hubbyesim.com/api/v1/bookings/{id}' \
  -H 'X-Api-Key: YOUR_API_KEY_HERE' \
  -H 'X-Signature: YOUR_API_KEY_HERE' \
  -H 'X-Timestamp: YOUR_API_KEY_HERE'

Responses

Headers
X-Hubby-Call-Idstringrequired

The id of this call in Hubby's records. Quote it when you ask Hubby about a call.

Bodyapplication/json
dataobject(Booking)required
data.​idstringrequired
data.​partner_idstringrequired
data.​visual_identity_idstring or nullrequired
data.​external_referencestring or nullrequired
data.​external_reference_secondarystring or nullrequired
data.​sourcestring(BookingSource)required
Enum"api""platform""manual""synthetic""migrated""free_esim""import""membership""store"
data.​depart_atstring or null(date-time)required
data.​return_atstring or null(date-time)required
data.​localestring or nullrequired
data.​emailstring or nullrequired
data.​phonestring or nullrequired
data.​first_namestring or nullrequired
data.​last_namestring or nullrequired
data.​full_namestring or nullrequired
data.​titlestring or nullrequired
data.​genderstring or nullrequired
data.​date_of_birthstring or nullrequired
data.​paxinteger or nullrequired
data.​flight_numberstring or nullrequired
data.​departure_locationstring or nullrequired
data.​communication_optionsobject or nullrequired

The communication_options you sent. Hubby stores and returns it; it does not control Hubby's messages. Hubby emails a traveller only when Hubby has turned on emails for your account, by agreement.

data.​communication_options.​should_send_messageboolean
data.​communication_options.​channelsArray of strings
data.​cancelled_atstring or null(date-time)required
data.​is_cancellablebooleanrequired

Whether DELETE /v1/bookings/{id} would cancel this booking now. False once it is cancelled, after depart_at, while any item is issued, or while a traveller's money sits on an item. Read it before offering a cancel button.

data.​is_pseudonymizedbooleanrequired
data.​itemsArray of objects(BookingItem)required

The booking's items, one per package specification. Each item's id is the {item} you issue, read and terminate its package with.

data.​items[].​idstringrequired
data.​items[].​codestring or null

The human-typeable redemption code for this package, twelve characters from an unambiguous alphabet. It is a bearer credential: whoever redeems it is bound to the package, so treat it like a voucher and do not put it anywhere public. Absent when your End-customer channel is partner_app.

data.​items[].​offer_uuidstring or null

The key of this item's upgrade-offer page, where the traveller can buy a larger package before redeeming. Distinct from id: id is what the outbound webhooks send as package_queue_uuid, this is the page's own token, and the two are never interchangeable. Use it only if you build the link yourself — upgrade_url is already the whole link. Absent when your End-customer channel is partner_app.

data.​items[].​upgrade_urlstring or null

The traveller's upgrade-offer link for this item, ready to send. It is a bearer capability URL — whoever opens it can buy an upgrade for this package — so treat it like code and keep it out of anywhere public. Null only on an environment with no public-pages deployment configured. Absent when your End-customer channel is partner_app.

data.​items[].​deep_link_urlstring or null

A universal link that opens the Hubby app and applies this item's code automatically, so a traveller never types it. On a device without the app it leads to the store instead; on desktop, to a download page — it is a mobile CTA, not a general redemption URL. Send it as given rather than rebuilding it. Null for an item carrying no code. Absent when your End-customer channel is partner_app.

data.​items[].​external_user_idstring or null
data.​items[].​emailstring or null
data.​items[].​customer_idstring or nullrequired
data.​items[].​destinationstring or null

The country's ISO 3166-1 alpha-2 code, or the region's code (for example EU). List the region codes from GET /v1/destinations.

data.​items[].​package_typePackageType (string) or nullrequired
Any of:
string(PackageType)
Enum"data_limited""starter""unlimited"
data.​items[].​size_bytesinteger or nullrequired

The size you asked for, in bytes: the size of the specification, or the bundle's size when you named a bundle_id. Not always the size issued: Hubby picks the closest bundle of the same type at issue, so read the issued size from the bundle package.bundle_id names. Null for a specification with no size.

data.​items[].​duration_secondsinteger or nullrequired
data.​items[].​start_atstring or null(date-time)required
data.​items[].​end_atstring or null(date-time)required
data.​items[].​statusstring(PackageQueueStatus)required

Deprecated: read state instead. state covers the whole lifecycle of the item, including the package issued from it.

Enum"queued""pending_payment""paid""activated""cancelled""reverted""unresolved"
data.​items[].​bundle_idstring or nullrequired

The bundle this item books: the one named by bundle_id at booking, or, once issued, the bundle the closest match chose. Null while an item specified by dimensions is not issued yet.

data.​items[].​statestring(PackageLifecycleState)required

Where this item is in its lifecycle. Read it instead of status: queued (reserved, not issued yet; you can issue it), unavailable (cannot be issued now: no bundle answers its specification, or a payment is still pending; cancel the booking or book again), redeemed (issued, install material available; with package: null, already spent, or brought over spent from the previous backend, so there is nothing to install), installed (the eSIM is on a phone), activated (live on the network, validity running), expired (the validity elapsed, or the provider ended the package after it activated; a package that used up its data stays activated until the provider ends it), terminated (ended before activation; on an item this is rare: when a package you issued ends before activation, the item returns to queued with package: null, so an item reads terminated only when the package showed data use before Hubby recorded its activation, or when the traveller redeemed the item in the Hubby app; treat it like expired), cancelled (the booking was cancelled, or the item's payment was rolled back).

Enum"queued""unavailable""redeemed""installed""activated""expired""terminated""cancelled"
data.​items[].​packageIssuedPackage (object) or nullrequired

The package issued for this item, the same body GET …/items/{item}/package answers. Null until it is issued.

Any of:

The package issued for this item, the same body GET …/items/{item}/package answers. Null until it is issued.

data.​items[].​package.​idstringrequired
data.​items[].​package.​statestring(PackageLifecycleState)required

Where this package is in its lifecycle: redeemed (issued, install material available), installed (the eSIM is on a phone), activated (live on the network, validity running), expired (the validity elapsed, or the provider ended the package after it activated; a package that used up its data stays activated until the provider ends it), terminated (ended before activation).

Enum"queued""unavailable""redeemed""installed""activated""expired""terminated""cancelled"
data.​items[].​package.​bundle_idstringrequired

The bundle this package was issued from.

data.​items[].​package.​issued_atstring or null(date-time)required

When the package was issued.

data.​items[].​package.​installed_atstring or null(date-time)required

When this package became installable on a phone: the eSIM's install, or the issue itself when the eSIM was already installed. Null until the eSIM is on a phone.

data.​items[].​package.​activated_atstring or null(date-time)required

When the package activated and its validity started: the time the connectivity provider reported the package activated. Hubby does not observe the network attach itself. Null until then.

data.​items[].​package.​expires_atstring or null(date-time)required

The latest moment this package can still be activated: the deadline the connectivity provider sets when the package is issued, after which an unused package can no longer be started. It is not when the package's validity ends: a package with a few days of validity is typically still activatable for about a year. Use valid_until for when the package stops working.

data.​items[].​package.​valid_untilstring or null(date-time)required

When this package's validity ends and its data stops working: the moment it activated plus its time allowance. Null until the package activates, because the clock starts at activation, not at issue. Also null for a package that carries no time allowance.

data.​items[].​package.​terminated_atstring or null(date-time)required

When the package ended: before activation (state is terminated), or after activation when the provider ended it (state is expired). Null while the package has not ended.

data.​items[].​package.​esimobject(EsimInstallMaterial)required
data.​items[].​package.​esim.​iccidstringrequired
data.​items[].​package.​esim.​installed_atstring or null(date-time)required

When this eSIM was first installed on a phone, whichever package it was installed for. Set for a repeat traveller: the eSIM is already on their phone, so skip the install screen and show that the new package is ready. Null until the eSIM is installed.

data.​items[].​package.​esim.​lpastring or nullrequired

The activation string. Render it as a QR code for "install on another device".

data.​items[].​package.​esim.​smdp_addressstring or nullrequired

The SM-DP+ address from lpa, for manual entry in the phone's settings.

data.​items[].​package.​esim.​matching_idstring or nullrequired

The matching id (activation code) from lpa, for manual entry in the phone's settings.

data.​items[].​package.​esim.​install_linksobjectrequired

One-tap install links. Open the one for the phone that will hold the eSIM; its own installer opens. Each is null when the activation string is not in the standard LPA:1$… form.

data.​created_atstring or null(date-time)required
data.​updated_atstring or null(date-time)required
Response
application/json
{ "data": { "id": "string", "partner_id": "string", "visual_identity_id": "string", "external_reference": "string", "external_reference_secondary": "string", "source": "api", "depart_at": "2019-08-24T14:15:22Z", "return_at": "2019-08-24T14:15:22Z", "locale": "string", "email": "string", "phone": "string", "first_name": "string", "last_name": "string", "full_name": "string", "title": "string", "gender": "string", "date_of_birth": "string", "pax": 0, "flight_number": "string", "departure_location": "string", "communication_options": { … }, "cancelled_at": "2019-08-24T14:15:22Z", "is_cancellable": true, "is_pseudonymized": true, "items": [ … ], "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z" } }

Partially update a booking: an omitted field is left as it is, and an explicit `null` clears it

Request

Changing depart_at or return_at reschedules the booking. If Hubby sends pre-departure emails for your account, they follow the new dates. A refused change writes nothing, including the other fields sent with it.

Path
idstringrequired
Headers
Idempotency-Keystring

Optional. A unique key for this request, for example a UUID, reused across every retry of it. A retry with the same key replays the stored success for 24 hours instead of running the request again.

Bodyapplication/json
external_referencestring or null<= 255 characters
external_reference_secondarystring or null<= 255 characters
visual_identity_idstring or null
depart_atstring or null

The trip's departure day, written DD/MM/YYYY, DD-MM-YYYY or YYYY-MM-DD (day first; an ISO 8601 date-time is accepted too). Changing it reschedules the booking. If Hubby sends pre-departure emails for your account, they follow the new day. Compared by calendar day, so resending the day the booking already has changes nothing. Answers 422 when the booking has already departed, when the day is before today, when it is null for a booking that has a departure, or when the booking is cancelled.

return_atstring or null

The trip's return day, written DD/MM/YYYY, DD-MM-YYYY or YYYY-MM-DD (day first; an ISO 8601 date-time is accepted too), on or after the departure. Changing it reschedules the booking, and may still be done once the trip is under way. Answers 422 for a cancelled booking.

localestring or null<= 10 characters

The booking's language as a BCP-47 tag. es, es-ES and es_es are all accepted and stored as one normalised tag. Up to 10 characters: a longer value is refused with 422. A value Hubby cannot read as a language is stored as null with no error, and your default_locale (GET /v1/me) then fills it.

emailstring or null(email)<= 255 characters
phonestring or null<= 50 characters
first_namestring or null<= 255 characters
last_namestring or null<= 255 characters
full_namestring or null<= 255 characters
titlestring or null<= 20 characters
genderstring or null<= 20 characters
date_of_birthstring or null(date)

The traveller's date of birth, a day before today, written YYYY-MM-DD. Answers return it as YYYY-MM-DD.

paxinteger or null[ 1 .. 2147483647 ]

How many travellers the booking is for. A headcount only: it creates no packages. Send one package specification per traveller.

flight_numberstring or null<= 20 characters
departure_locationstring or null<= 255 characters
communication_optionsobject or null

Your messaging preferences for the traveller. Hubby stores and returns it; it does not control Hubby's messages. should_send_message is required when the object is sent; channels is optional.

curl -i -X PATCH \
  'https://core.hubbyesim.com/api/v1/bookings/{id}' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: string' \
  -H 'X-Api-Key: YOUR_API_KEY_HERE' \
  -H 'X-Signature: YOUR_API_KEY_HERE' \
  -H 'X-Timestamp: YOUR_API_KEY_HERE' \
  -d '{
    "external_reference": "string",
    "external_reference_secondary": "string",
    "visual_identity_id": "string",
    "depart_at": "string",
    "return_at": "string",
    "locale": "string",
    "email": "user@example.com",
    "phone": "string",
    "first_name": "string",
    "last_name": "string",
    "full_name": "string",
    "title": "string",
    "gender": "string",
    "date_of_birth": "2019-08-24",
    "pax": 1,
    "flight_number": "string",
    "departure_location": "string",
    "communication_options": {
      "should_send_message": true,
      "channels": [
        "EMAIL"
      ]
    }
  }'

Responses

Headers
Idempotent-Replayedstring

Sent as true only when this answer is the stored answer to an earlier request with the same Idempotency-Key, replayed unchanged. Absent on the first answer.

X-Hubby-Call-Idstringrequired

The id of this call in Hubby's records. Quote it when you ask Hubby about a call.

Bodyapplication/json
dataobject(Booking)required
data.​idstringrequired
data.​partner_idstringrequired
data.​visual_identity_idstring or nullrequired
data.​external_referencestring or nullrequired
data.​external_reference_secondarystring or nullrequired
data.​sourcestring(BookingSource)required
Enum"api""platform""manual""synthetic""migrated""free_esim""import""membership""store"
data.​depart_atstring or null(date-time)required
data.​return_atstring or null(date-time)required
data.​localestring or nullrequired
data.​emailstring or nullrequired
data.​phonestring or nullrequired
data.​first_namestring or nullrequired
data.​last_namestring or nullrequired
data.​full_namestring or nullrequired
data.​titlestring or nullrequired
data.​genderstring or nullrequired
data.​date_of_birthstring or nullrequired
data.​paxinteger or nullrequired
data.​flight_numberstring or nullrequired
data.​departure_locationstring or nullrequired
data.​communication_optionsobject or nullrequired

The communication_options you sent. Hubby stores and returns it; it does not control Hubby's messages. Hubby emails a traveller only when Hubby has turned on emails for your account, by agreement.

data.​communication_options.​should_send_messageboolean
data.​communication_options.​channelsArray of strings
data.​cancelled_atstring or null(date-time)required
data.​is_cancellablebooleanrequired

Whether DELETE /v1/bookings/{id} would cancel this booking now. False once it is cancelled, after depart_at, while any item is issued, or while a traveller's money sits on an item. Read it before offering a cancel button.

data.​is_pseudonymizedbooleanrequired
data.​itemsArray of objects(BookingItem)required

The booking's items, one per package specification. Each item's id is the {item} you issue, read and terminate its package with.

data.​items[].​idstringrequired
data.​items[].​codestring or null

The human-typeable redemption code for this package, twelve characters from an unambiguous alphabet. It is a bearer credential: whoever redeems it is bound to the package, so treat it like a voucher and do not put it anywhere public. Absent when your End-customer channel is partner_app.

data.​items[].​offer_uuidstring or null

The key of this item's upgrade-offer page, where the traveller can buy a larger package before redeeming. Distinct from id: id is what the outbound webhooks send as package_queue_uuid, this is the page's own token, and the two are never interchangeable. Use it only if you build the link yourself — upgrade_url is already the whole link. Absent when your End-customer channel is partner_app.

data.​items[].​upgrade_urlstring or null

The traveller's upgrade-offer link for this item, ready to send. It is a bearer capability URL — whoever opens it can buy an upgrade for this package — so treat it like code and keep it out of anywhere public. Null only on an environment with no public-pages deployment configured. Absent when your End-customer channel is partner_app.

data.​items[].​deep_link_urlstring or null

A universal link that opens the Hubby app and applies this item's code automatically, so a traveller never types it. On a device without the app it leads to the store instead; on desktop, to a download page — it is a mobile CTA, not a general redemption URL. Send it as given rather than rebuilding it. Null for an item carrying no code. Absent when your End-customer channel is partner_app.

data.​items[].​external_user_idstring or null
data.​items[].​emailstring or null
data.​items[].​customer_idstring or nullrequired
data.​items[].​destinationstring or null

The country's ISO 3166-1 alpha-2 code, or the region's code (for example EU). List the region codes from GET /v1/destinations.

data.​items[].​package_typePackageType (string) or nullrequired
Any of:
string(PackageType)
Enum"data_limited""starter""unlimited"
data.​items[].​size_bytesinteger or nullrequired

The size you asked for, in bytes: the size of the specification, or the bundle's size when you named a bundle_id. Not always the size issued: Hubby picks the closest bundle of the same type at issue, so read the issued size from the bundle package.bundle_id names. Null for a specification with no size.

data.​items[].​duration_secondsinteger or nullrequired
data.​items[].​start_atstring or null(date-time)required
data.​items[].​end_atstring or null(date-time)required
data.​items[].​statusstring(PackageQueueStatus)required

Deprecated: read state instead. state covers the whole lifecycle of the item, including the package issued from it.

Enum"queued""pending_payment""paid""activated""cancelled""reverted""unresolved"
data.​items[].​bundle_idstring or nullrequired

The bundle this item books: the one named by bundle_id at booking, or, once issued, the bundle the closest match chose. Null while an item specified by dimensions is not issued yet.

data.​items[].​statestring(PackageLifecycleState)required

Where this item is in its lifecycle. Read it instead of status: queued (reserved, not issued yet; you can issue it), unavailable (cannot be issued now: no bundle answers its specification, or a payment is still pending; cancel the booking or book again), redeemed (issued, install material available; with package: null, already spent, or brought over spent from the previous backend, so there is nothing to install), installed (the eSIM is on a phone), activated (live on the network, validity running), expired (the validity elapsed, or the provider ended the package after it activated; a package that used up its data stays activated until the provider ends it), terminated (ended before activation; on an item this is rare: when a package you issued ends before activation, the item returns to queued with package: null, so an item reads terminated only when the package showed data use before Hubby recorded its activation, or when the traveller redeemed the item in the Hubby app; treat it like expired), cancelled (the booking was cancelled, or the item's payment was rolled back).

Enum"queued""unavailable""redeemed""installed""activated""expired""terminated""cancelled"
data.​items[].​packageIssuedPackage (object) or nullrequired

The package issued for this item, the same body GET …/items/{item}/package answers. Null until it is issued.

Any of:

The package issued for this item, the same body GET …/items/{item}/package answers. Null until it is issued.

data.​items[].​package.​idstringrequired
data.​items[].​package.​statestring(PackageLifecycleState)required

Where this package is in its lifecycle: redeemed (issued, install material available), installed (the eSIM is on a phone), activated (live on the network, validity running), expired (the validity elapsed, or the provider ended the package after it activated; a package that used up its data stays activated until the provider ends it), terminated (ended before activation).

Enum"queued""unavailable""redeemed""installed""activated""expired""terminated""cancelled"
data.​items[].​package.​bundle_idstringrequired

The bundle this package was issued from.

data.​items[].​package.​issued_atstring or null(date-time)required

When the package was issued.

data.​items[].​package.​installed_atstring or null(date-time)required

When this package became installable on a phone: the eSIM's install, or the issue itself when the eSIM was already installed. Null until the eSIM is on a phone.

data.​items[].​package.​activated_atstring or null(date-time)required

When the package activated and its validity started: the time the connectivity provider reported the package activated. Hubby does not observe the network attach itself. Null until then.

data.​items[].​package.​expires_atstring or null(date-time)required

The latest moment this package can still be activated: the deadline the connectivity provider sets when the package is issued, after which an unused package can no longer be started. It is not when the package's validity ends: a package with a few days of validity is typically still activatable for about a year. Use valid_until for when the package stops working.

data.​items[].​package.​valid_untilstring or null(date-time)required

When this package's validity ends and its data stops working: the moment it activated plus its time allowance. Null until the package activates, because the clock starts at activation, not at issue. Also null for a package that carries no time allowance.

data.​items[].​package.​terminated_atstring or null(date-time)required

When the package ended: before activation (state is terminated), or after activation when the provider ended it (state is expired). Null while the package has not ended.

data.​items[].​package.​esimobject(EsimInstallMaterial)required
data.​items[].​package.​esim.​iccidstringrequired
data.​items[].​package.​esim.​installed_atstring or null(date-time)required

When this eSIM was first installed on a phone, whichever package it was installed for. Set for a repeat traveller: the eSIM is already on their phone, so skip the install screen and show that the new package is ready. Null until the eSIM is installed.

data.​items[].​package.​esim.​lpastring or nullrequired

The activation string. Render it as a QR code for "install on another device".

data.​items[].​package.​esim.​smdp_addressstring or nullrequired

The SM-DP+ address from lpa, for manual entry in the phone's settings.

data.​items[].​package.​esim.​matching_idstring or nullrequired

The matching id (activation code) from lpa, for manual entry in the phone's settings.

data.​items[].​package.​esim.​install_linksobjectrequired

One-tap install links. Open the one for the phone that will hold the eSIM; its own installer opens. Each is null when the activation string is not in the standard LPA:1$… form.

data.​created_atstring or null(date-time)required
data.​updated_atstring or null(date-time)required
Response
application/json
{ "data": { "id": "string", "partner_id": "string", "visual_identity_id": "string", "external_reference": "string", "external_reference_secondary": "string", "source": "api", "depart_at": "2019-08-24T14:15:22Z", "return_at": "2019-08-24T14:15:22Z", "locale": "string", "email": "string", "phone": "string", "first_name": "string", "last_name": "string", "full_name": "string", "title": "string", "gender": "string", "date_of_birth": "string", "pax": 0, "flight_number": "string", "departure_location": "string", "communication_options": { … }, "cancelled_at": "2019-08-24T14:15:22Z", "is_cancellable": true, "is_pseudonymized": true, "items": [ … ], "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z" } }

Cancel a booking and remove the traveller's personal data from it

Request

{id} accepts the same identifiers as GET /v1/bookings/{id}. A booking that cannot be cancelled is refused with 403 and a code; is_cancellable on every booking read tells you before you call. A repeat DELETE of a cancelled booking answers 200 and changes nothing.

An issued package that has not activated can be terminated first (DELETE /v1/bookings/{booking}/items/{item}/package), after which the cancel goes through. Once a package has activated on the network, or when an item was brought over from the previous backend already redeemed, the booking can no longer be cancelled.

Path
idstringrequired
Headers
Idempotency-Keystring

Optional. A unique key for this request, for example a UUID, reused across every retry of it. A retry with the same key replays the stored success for 24 hours instead of running the request again.

curl -i -X DELETE \
  'https://core.hubbyesim.com/api/v1/bookings/{id}' \
  -H 'Idempotency-Key: string' \
  -H 'X-Api-Key: YOUR_API_KEY_HERE' \
  -H 'X-Signature: YOUR_API_KEY_HERE' \
  -H 'X-Timestamp: YOUR_API_KEY_HERE'

Responses

Headers
Idempotent-Replayedstring

Sent as true only when this answer is the stored answer to an earlier request with the same Idempotency-Key, replayed unchanged. Absent on the first answer.

X-Hubby-Call-Idstringrequired

The id of this call in Hubby's records. Quote it when you ask Hubby about a call.

Bodyapplication/json
messagestringrequired
Value "Booking cancelled successfully."
Response
application/json
{ "message": "Booking cancelled successfully." }

BookingItemPackage

Operations

Bundle

Operations

CurrentPartner

Operations

Destination

Operations

DeviceCompatibility

Operations

Esim

Operations

RedirectToken

Operations