Hubby API (2.1.0)

Welcome to the Hubby API documentation. This API enables partners to seamlessly integrate eSIM booking and management capabilities into their applications.

Key Features:

  • Create and manage eSIM bookings for your customers
  • Access our global package catalog with country-specific offerings
  • Track booking statuses and package activations
  • Support for multiple package types (starter, data-limited, unlimited)

Package Types

The API supports four types of packages:

  • Starter packages: Hybrid packages that are both data and time limited. They provide a small data allowance within a short time period (default: 2 days). Perfect for first-time users and trials.

  • Data-limited packages: Traditional packages with a specific data allowance that expires after a certain period (default: 365 days). This is the primary package type and the default for most use cases.

  • Unlimited packages: Packages that provide unrestricted data usage for a specified duration. Subject to fair use policy. Ideal for heavy data users and digital nomads.

  • Time-limited packages (deprecated): Packages that provide a fixed data allowance for a specific duration with full-speed access. They expire when either the data limit or time limit is reached. This package type is deprecated and should not be used for new integrations — use a data-limited package with a short package_duration instead.

Note: Top-ups are always data-limited packages, regardless of the original package type.

Authentication: All API requests must include the following headers:

  • x-api-key: Your public API key
  • x-timestamp: Current Unix timestamp in milliseconds
  • x-signature: HMAC-SHA256 signature

The HMAC signature must be generated for each request using:

  1. Concatenate: timestamp + HTTP method + request path + query string (if any) The request path is the full path after the domain and must include the /api prefix. Example: "1678901234GET/api/bookings"
  2. Generate HMAC-SHA256 using your secret key
  3. Convert to hex string

Note: Swagger UI cannot be used to test the API directly as each request requires a unique HMAC signature. Please implement the authentication in your client application.

Example Node.js Implementation:

const cryptoJs = require('crypto-js');

// Configuration values that would normally come from environment
const secretKey = "YOUR_API_SECRET";
const publicKey = "YOUR_API_KEY";

// Generate headers for an API request.
// `url` is the full request URL, e.g. "https://api.hubbyesim.com/api/bookings?perPage=10"
function generateApiHeaders(method, url) {
    // Timestamp is in milliseconds e.g. 1715558400000
    const timestamp = Math.floor(Date.now()).toString();

    const parsed = new URL(url);

    // The signed path is everything after the domain, INCLUDING the /api
    // prefix, plus the query string if present.
    const pathWithQuery = parsed.pathname + parsed.search;

    // Sample payload: 1715558400000GET/api/bookings?perPage=10
    const payload = timestamp + method + pathWithQuery;

    // Generate the HMAC signature
    const signature = cryptoJs.HmacSHA256(payload, secretKey).toString(cryptoJs.enc.Hex);

    return {
        'x-timestamp': timestamp,
        'x-signature': signature,
        'x-api-key': publicKey,
        'Accept': 'application/json'
    };
}

Webhook Delivery Authentication

When Hubby delivers a webhook to your endpoint, the request carries your API key in a header. By default this is x-api-key: <your-key>. If your endpoint expects the key under a different header, the header name is configurable per partner (for example Authorization) with an optional value prefix (for example Bearer , producing Authorization: Bearer <your-key>). The default remains x-api-key with no prefix — contact support@hubbyesim.com to change it.

Signature verification

When a signing secret is configured, every webhook is signed so you can verify it originated from Hubby and was not altered in transit. Each delivery carries these headers:

  • x-hubby-signature — one or more comma-separated sha256=<hex> values. Verify by recomputing HMAC-SHA256(secret, "{timestamp}.{body}"), hex-encoding the result, and accepting the request if it matches any entry in the list. Signatures are lowercase hex. Treat this as a list from day one: today we send a single signature, but the list form lets us add a second signature during a future secret rotation without any change on your side.
  • x-hubby-timestamp — the signing timestamp, in epoch seconds. This is the {timestamp} in the signed string above.

The signed string is the timestamp, a literal ., then the exact raw request body as received. Verify against the raw bytes — do not re-serialize the JSON first, as key reordering or whitespace changes will break the match.

Reject the request if now - x-hubby-timestamp exceeds your tolerance window (we recommend ~5 minutes). This is your protection against replayed requests.

A secret stays valid until it is regenerated; there is no scheduled expiry. Regeneration is a hard cutover — update your stored secret at the same time. You receive separate secrets for staging and production.

Idempotency & event identity

Two identifiers accompany every delivery, both as headers and inside the JSON body (in case reading custom headers is awkward in your stack):

  • x-hubby-event-id / event_id — a stable identifier for the logical event. It stays identical across retries and across any replay of the same event, so deduplicate on it: processing the same event_id more than once should be a no-op on your side.
  • x-hubby-delivery-id / delivery_id — identifies a single delivery. Quote it when contacting support about a specific delivery. A replay of an event carries the same event_id but a new delivery_id.

Delivery, retries & replay

  • A delivery is considered successful when your endpoint returns any 2xx status.
  • If your endpoint returns 5xx or 429, times out, or is unreachable, Hubby retries with exponential backoff over a few hours (up to 12 attempts total).
  • A 4xx (other than 429) is treated as a permanent rejection of the content and is not retried. Return 2xx for "received" and reserve 4xx for genuinely malformed requests.
  • Every delivery is recorded and any past event can be replayed to you on request. A replay carries the same event_id (so your dedup keeps it safe) with a fresh signature and timestamp.
  • Deliveries are independent and ordering is not guaranteed; rely on event_id for deduplication rather than on arrival order.

Need Help?

  • Technical Support: support@hubbyesim.com
Download OpenAPI description
Languages
Servers
Mock server
https://docs.hubbyesim.com/_mock/apis/openapi/
Production server
https://api.hubbyesim.com/api/
Staging server
https://api-staging.hubby.dev/api/

Booking

Operations

List all bookings

Request

Retrieves a paginated list of bookings for the authenticated partner, ordered by creation date (newest first). The response is always fresh and not cached (uses Cache-Control headers).

Results can be narrowed with the optional filter parameters below. Note: when filtering by destination, the response is not cursor-paginated (all matches are returned in one page).

Query
perPageinteger

Number of records to return per page (minimum 1)

Default 10
cursorstring

Cursor for pagination (use nextCursor/prevCursor from previous response)

emailstring(email)

Return only bookings with this contact email (case-insensitive).

booking_idstring

Return only bookings with this external booking ID.

departure_datestring(date)

Return only bookings with exactly this departure date.

created_atstring(date)

Return only bookings created on this calendar day.

promo_codesstring

Return only the booking(s) containing this promo code.

destinationstring

Return only bookings whose first package targets this destination — either a destination name (e.g. "Spain") or an ISO3 code (e.g. "ESP"). This filter is matched via the booking's promo codes and returns a single non-paginated page.

sortstring

Booking field to sort by (e.g. departure_date). Must be combined with order; default sorting is by created_at descending.

orderstring

Sort direction, used together with sort.

Enum"asc""desc"
curl -i -X GET \
  'https://docs.hubbyesim.com/_mock/apis/openapi/bookings?booking_id=string&created_at=2019-08-24&cursor=string&departure_date=2019-08-24&destination=string&email=user%40example.com&order=asc&perPage=10&promo_codes=string&sort=string'

Responses

A paginated list of bookings

Bodyapplication/json
successboolean
Example: true
messagestring
Example: "Success message"
dataArray of objects(BookingResponse)
paginationobject(PaginationMeta)
Response
application/json
{ "success": true, "message": "Success message", "data": [ {} ], "pagination": { "perPage": 0, "hasNextPage": true, "hasPrevPage": true, "nextCursor": "string", "prevCursor": "string" } }

Create a new booking

Request

Creates a new booking with the specified details. The booking is identified by the email or booking_id parameter (at least one of the two is required). At least one package specification is always required; a specification without a destination creates a destination-agnostic package, and the traveler picks the destination at redemption.

Fixed package strategy. If your partner account is configured with a fixed package strategy, the size, package_type, and package_duration of each incoming specification are replaced by the configured strategy values — only destination and external_user_id are taken from the request.

Re-submitting a booking (idempotent merge). For user-keyed bookings — where a package_specification carries an external_user_id (webapp flow) or email (native-grant flow) — re-submitting with the same booking_id, departure_date, and partner merges any new package_specifications into the existing booking (provisioning the additional package queues) instead of creating a duplicate. Specifications already provisioned for that user are skipped, so retries are a no-op and incremental additions (a new destination, or a second passenger on the same booking) are safe. Bookings that carry no user key (promo-code flow) are unaffected: a matching re-submission returns the existing booking and ignores the new specifications.

Bodyapplication/jsonrequired
departure_datestring(date-time)required

ISO 8601 departure date. Example: '2026-01-03'.

Always required — including for bookings that don't represent a real trip (e.g. classic-eSIM grants for loyalty rewards or refunds); send the current date in that case.

return_datestring(date-time)

ISO 8601 return date. Must be after departure_date. Optional if not provided by the partner.

emailstring(email)

Traveler's email. Required if booking_id is not provided.

booking_idstring>= 3 characters

Booking ID. Required if email is not provided.

external_idstring or null[ 3 .. 100 ] characters

External ID Usable by your organization to further identify the booking (optional).

phonestring^\+\d{1,3}\d{1,14}$

Phone number in E.164 format (e.g., +123456789).

first_namestring[ 1 .. 100 ] characters
last_namestring[ 1 .. 100 ] characters
full_namestring[ 1 .. 200 ] characters
titlestring
Enum"mr.""ms.""mrs.""miss""dr.""prof."
paxinteger>= 1

Number of passengers.

flight_numberstring^[a-zA-Z0-9]{1,10}$
genderstring
Enum"M""F""O"
localestring[ 2 .. 5 ] characters

Locale or language code (e.g., "en", "nl"). When omitted, defaults to your partner account's configured booking locale, falling back to "en".

dataobject

Additional metadata about the booking. Only the keys below are stored; unknown keys are discarded.

communication_optionsobject

Optional communication preferences for sending messages. If not provided, messages will not be sent.

custom_brandingstring or null

Sets a specific visual style for the app experience. The brand identifier must correspond to a brand configuration that exists in the Hubby platform. Contact support to set up custom branding for your organization.

departure_locationstring or null

The departure location for the booking (e.g., airport code, city name, or address). Used to provide context about where the traveler is departing from.

package_specificationsArray of objectsnon-emptyrequired

A list of package specifications. At least one entry is required.

Each entry describes one package to grant, resolved from bundle_id (a specific destination bundle) or from its package_type/size/package_duration fields. package_type is required on every entry unless one of these defaults applies:

  • destination + size sent → defaults to data-limited
  • only destination sent → defaults to starter (1GB, 2 days)
  • bundle_id sent → type/size/duration are taken from the bundle

Note that package_id does not trigger a default: entries carrying a package_id must still send an explicit package_type and that type's required fields, otherwise the request fails with 422 — package_type is required.

Entries without any destination (destination/iata_code) are accepted when package_type+size requirements are met: the package is created destination-agnostic and the traveler picks the destination at redemption.

package_specifications[].​external_user_idstring

Your own identifier for the traveler receiving this package.

⚠️ This field is required for Hubby WebView and the universal-eSIM Native Integration. Omit it for all other integration types — it is ignored in the traditional booking flow (email-based delivery, promo codes, etc.) and supplying it incorrectly will route the package into the eSIM management flow.

  • With external_user_id (required for WebView and universal Native): The package is linked to the traveler's universal eSIM. Use the same value across all subsequent calls (redirect tokens, dashboard, redeem, top-ups).
  • Without external_user_id (traditional flow): The package follows the standard booking flow. The field is ignored.
  • Classic-eSIM grant flow: Pair esim_type: 'classic' with either external_user_id or email (per-spec). The traveler claims the resulting queue via the Hubby SDK's classic onCalls in your native app.
Example: "user_abc123"
package_specifications[].​emailstring(email)

Per-spec traveler email, used as an alternative to external_user_id when you don't have a stable per-user identifier on your side (e.g. loyalty rewards, refund grants, retention plays).

When set, the spec routes through the user-keyed booking path: Hubby find-or-creates a User scoped to your Partner (same email under a different Partner produces a separate User — partner-scoped uniqueness). When both external_user_id and email are present on the same spec, external_user_id wins for identity; email is then used only for first-time Firebase user creation and contact.

Distinct from the top-level email field on the Booking, which is for booking-confirmation messaging only and does not key a User.

Example: "traveler@example.com"
package_specifications[].​esim_typestring

Selects the redemption surface for this spec.

  • universal (default — back-compat): The package is queued onto the traveler's single multi-IMSI universal eSIM and redeemed via the WebView. Requires external_user_id.
  • classic: A single-IMSI eSIM is provisioned (or topped up onto an existing matching one) at claim time via the native app's classic onCalls. Requires either external_user_id or email on this spec. Pair with optional expires_at to bound how long the grant is redeemable.
Default "universal"
Enum"universal""classic"
package_specifications[].​expires_atstring(date-time)

ISO 8601 timestamp after which a classic-eSIM grant can no longer be claimed. Only meaningful when esim_type: 'classic'. When omitted, defaults server-side to 90 days from booking creation. Ignored for esim_type: 'universal' (universal queues do not expire).

Example: "2026-09-01T00:00:00Z"
package_specifications[].​destinationstring or Array of strings

Destination code(s) for the booking. Can be a single destination or an array of destinations for multi-destination trips with ESIM optimization. Optional — omit (together with iata_code) to create a destination-agnostic package the traveler resolves at redemption.

One of:

Destination code(s) for the booking. Can be a single destination or an array of destinations for multi-destination trips with ESIM optimization. Optional — omit (together with iata_code) to create a destination-agnostic package the traveler resolves at redemption.

string^[A-Z]{2,3}$

Destination code(s) for the booking. Can be a single destination or an array of destinations for multi-destination trips with ESIM optimization. Optional — omit (together with iata_code) to create a destination-agnostic package the traveler resolves at redemption.

package_specifications[].​iata_codestring

IATA airport code (e.g. 'AMS', 'JFK') as an alternative to destination — the destination country is resolved from the airport. Ignored when destination is present.

Example: "AMS"
package_specifications[].​sizestring^[0-9]\d*(\.\d+)?(MB|GB)$

Package size as a number followed by MB or GB (e.g., '500MB', '1GB', '3GB', '5GB', '10GB', '20GB'). Required when package_type is data-limited or starter (explicit or defaulted). Not used for unlimited.

package_specifications[].​package_idstring

Identifier of an existing Hubby package to grant (optional). Must be accompanied by an explicit package_type and that type's required fields (package_id alone is rejected with 422 — package_type is required.). Destination validation is skipped for entries carrying a package_id.

package_specifications[].​bundle_idstring

Identifier of a specific destination bundle (listed in the bundles array of GET /destinations/{id}), e.g. DATA_LIMITED_10GB_365_DAYS_ESP. When present, package_type/size/package_duration are not required — the bundle defines them. The entry must still resolve a destination: send destination or iata_code alongside, unless the bundle id itself ends in an ISO3 country code (as in the example), which is then used automatically. The bundle must exist and be active for that destination.

Example: "DATA_LIMITED_10GB_365_DAYS_ESP"
package_specifications[].​package_typestring

Type of package to create. Required unless a default applies (see package_specifications above) or bundle_id is used.

  • data-limited: Traditional packages with a specific data allowance. Requires size.
  • unlimited: Unrestricted data usage under a fair-use policy. Requires package_duration; size is not used.
  • starter: Small data allowance for a short duration. Requires size; package_duration defaults to 2 days.
  • time-limited (deprecated — use data-limited with a package_duration): Fixed data allowance that expires after a set duration with full-speed access.
Enum"starter""data-limited""unlimited""time-limited"
package_specifications[].​package_durationinteger>= 1

Duration of the package in days.

  • Required for unlimited packages
  • Defaults to 365 days for data-limited packages
  • Defaults to 2 days for starter packages
curl -i -X POST \
  https://docs.hubbyesim.com/_mock/apis/openapi/bookings \
  -H 'Content-Type: application/json' \
  -d '{
    "departure_date": "2019-08-24T14:15:22Z",
    "return_date": "2019-08-24T14:15:22Z",
    "email": "user@example.com",
    "booking_id": "string",
    "external_id": "string",
    "phone": "string",
    "first_name": "string",
    "last_name": "string",
    "full_name": "string",
    "title": "mr.",
    "pax": 1,
    "flight_number": "string",
    "gender": "M",
    "locale": "strin",
    "data": {
      "source": "string",
      "manual": true,
      "action": "string",
      "tags": [
        "string"
      ]
    },
    "communication_options": {
      "should_send_message": true,
      "channels": [
        "EMAIL"
      ]
    },
    "custom_branding": "string",
    "departure_location": "string",
    "package_specifications": [
      {
        "external_user_id": "user_abc123",
        "email": "traveler@example.com",
        "esim_type": "universal",
        "expires_at": "2026-09-01T00:00:00Z",
        "destination": "string",
        "iata_code": "AMS",
        "size": "string",
        "package_id": "string",
        "bundle_id": "DATA_LIMITED_10GB_365_DAYS_ESP",
        "package_type": "starter",
        "package_duration": 1
      }
    ]
  }'

Responses

Successful response

Bodyapplication/json
successboolean
Example: true
messagestring
Example: "Booking created successfully."
dataobject

The created (or merged/duplicate-matched) booking. Delivery handles depend on the flow: user-keyed bookings (any spec carrying external_user_id or email) return package_queues; traditional bookings return promo_codes.

Response
application/json
{ "success": true, "message": "Booking created successfully.", "data": { "external_id": "string", "id": "string", "title": "string", "first_name": "string", "last_name": "string", "full_name": "string", "pax": 0, "email": "user@example.com", "phone": "string", "booking_id": "string", "flight_number": "string", "partner": "string", "status": "PENDING", "brand": "string", "return_date": "2019-08-24T14:15:22Z", "departure_date": "2019-08-24T14:15:22Z", "gender": "M", "locale": "string", "promo_codes": [], "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "created_by": "string", "updated_by": "string", "custom_branding": "string", "departure_location": "string", "package_queues": [] } }

Get booking details

Request

Retrieves details of a specific booking

Path
idstringrequired

Booking ID

curl -i -X GET \
  'https://docs.hubbyesim.com/_mock/apis/openapi/bookings/{id}'

Responses

Successful response

Bodyapplication/json
successboolean
Example: true
messagestring
Example: "Success message"
dataobject(BookingResponse)
Response
application/json
{ "success": true, "message": "Success message", "data": { "external_id": "string", "id": "string", "title": "string", "first_name": "string", "last_name": "string", "full_name": "string", "pax": 0, "email": "user@example.com", "phone": "string", "booking_id": "string", "flight_number": "string", "partner": "string", "status": "PENDING", "brand": "string", "return_date": "2019-08-24T14:15:22Z", "departure_date": "2019-08-24T14:15:22Z", "gender": "M", "locale": "string", "promo_codes": [], "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "created_by": "string", "updated_by": "string", "custom_branding": "string", "departure_location": "string", "package_queues": [] } }

Cancel booking

Request

Cancels a specific booking by setting its status to CANCELLED and anonymizing all personal data fields.

The following fields are hashed with MD5 using a configurable salt (ANONYMIZATION_SALT environment variable): first_name, last_name, full_name, phone, flight_number, departure_location. The email field is replaced with a non-identifiable address. Fields title, gender, and pax are cleared. The booking_id and external_id are preserved intact.

The booking record is not deleted from the database.

Cancellation is refused (403) when the booking can no longer be safely cancelled: the departure date is in the past, messages have already been sent for it, or one of its promo codes has already been used.

Path
idstringrequired

Booking ID

curl -i -X DELETE \
  'https://docs.hubbyesim.com/_mock/apis/openapi/bookings/{id}'

Responses

Successful response

Bodyapplication/json
successboolean
Example: true
messagestring
Example: "Booking cancelled successfully."
dataobject

Empty object

Example: {}
Response
application/json
{ "success": true, "message": "Booking cancelled successfully.", "data": {} }

Cancel booking by booking ID

Request

Cancels a booking using its external booking ID by setting its status to CANCELLED and anonymizing all personal data fields.

The following fields are hashed with MD5 using a configurable salt (ANONYMIZATION_SALT environment variable): first_name, last_name, full_name, phone, flight_number, departure_location. The email field is replaced with a non-identifiable address. Fields title, gender, and pax are cleared. The booking_id and external_id are preserved intact.

The booking record is not deleted from the database.

Path
idstringrequired

External booking ID (booking_id)

curl -i -X DELETE \
  'https://docs.hubbyesim.com/_mock/apis/openapi/bookings/{id}/byExternalBookingId'

Responses

Successful response

Bodyapplication/json
successboolean
Example: true
messagestring
Example: "Success message"
dataobject

Empty object

Example: {}
Response
application/json
{ "success": true, "message": "Success message", "data": {} }

Update all promo codes for a booking

Request

Updates the package_specification on all promo codes for the booking. Promo codes that have already been used are skipped; the response includes updated_count and skipped_count.

Authentication: HMAC keys.

Path
booking_idstringrequired

The external booking ID (e.g. "BK-12345").

Bodyapplication/jsonrequired
package_specificationobject(PackageSpecification)required

Package specification for updating promo codes. At least one of package_id or package_type must be provided.

When only package_id is sent: The package's properties (package_type, size, package_duration, destination) are resolved automatically from the referenced package.

When package_id is sent with package_type: The explicit fields from the request body are used instead of the package's own properties.

When only package_type is sent: The conditional field requirements apply based on the type.

Package type requirements:

  • data-limitedsize: required, package_duration: not required
  • time-limited (deprecated — use data-limited with a package_duration)size: required, package_duration: required
  • unlimitedsize: not required, package_duration: required
  • startersize: required, package_duration: required
package_specification.​package_idstring

ID of an existing package. At least one of package_id or package_type must be provided.

When package_id is provided without package_type: The package's properties (package_type, size, package_duration, destination) are resolved automatically from the referenced package.

When package_id is provided with package_type: The explicit fields from the request body are used instead of the package's own properties.

package_specification.​package_typestring

Type of package. At least one of package_id or package_type must be provided.

  • starter: Small data allowance for short duration, ideal for trials
  • data-limited: Traditional packages with specific data allowance
  • unlimited: Unrestricted data usage with fair use policy
  • time-limited (deprecated — use data-limited with a package_duration): Fixed data allowance with time constraint for full-speed access
Enum"starter""data-limited""unlimited""time-limited"
package_specification.​sizestring^[0-9]\d*(\.\d+)?(MB|GB)$

Package size (e.g. "1GB", "5GB"). Required when package_type is data-limited, time-limited, or starter. Not required for unlimited.

package_specification.​package_durationinteger>= 1

Duration of the package in days. Required when package_type is time-limited, unlimited, or starter. Not required for data-limited.

package_specification.​destinationstring or Array of strings

Country ISO3 code or IATA code, or an array for multi-destination. Optional — omit for destination-agnostic promo codes.

One of:

Country ISO3 code or IATA code, or an array for multi-destination. Optional — omit for destination-agnostic promo codes.

string^[A-Z]{2,3}$

Country ISO3 code or IATA code, or an array for multi-destination. Optional — omit for destination-agnostic promo codes.

curl -i -X PUT \
  'https://docs.hubbyesim.com/_mock/apis/openapi/bookings/{booking_id}/updatePackageSpecifications' \
  -H 'Content-Type: application/json' \
  -d '{
    "package_specification": {
      "package_id": "string",
      "package_type": "starter",
      "size": "string",
      "package_duration": 1,
      "destination": "ESP"
    }
  }'

Responses

Successfully updated package specifications. When some promo codes were skipped (already redeemed), message is "Updated N promo code(s). Skipped M already redeemed." and data includes both updated_count and skipped_count. When all promo codes were updated, message is "Successfully updated package specification for booking." and skipped_count is omitted.

Bodyapplication/json
successboolean
Example: true
messagestring
Example: "Updated 3 promo code(s). Skipped 1 already redeemed."
dataobject(UpdatePackageSpecForPromoCodesResponse)
Response
application/json
{ "success": true, "message": "Updated 3 promo code(s). Skipped 1 already redeemed.", "data": { "external_id": "string", "id": "string", "title": "string", "first_name": "string", "last_name": "string", "full_name": "string", "pax": 0, "email": "user@example.com", "phone": "string", "booking_id": "string", "flight_number": "string", "partner": "string", "status": "PENDING", "brand": "string", "return_date": "2019-08-24T14:15:22Z", "departure_date": "2019-08-24T14:15:22Z", "gender": "M", "locale": "string", "promo_codes": [], "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "created_by": "string", "updated_by": "string", "custom_branding": "string", "departure_location": "string", "package_queues": [], "updated_count": 0, "skipped_count": 0 } }

Update a single promo code by UUID

Request

Updates the package_specification on only the promo code whose uuid matches the path parameter. If that promo code is already redeemed, the API returns 400 Bad Request.

Authentication: HMAC keys.

Path
booking_idstringrequired

The external booking ID.

uuidstring(uuid)required

The promo code's UUID.

Bodyapplication/jsonrequired
package_specificationobject(PackageSpecification)required

Package specification for updating promo codes. At least one of package_id or package_type must be provided.

When only package_id is sent: The package's properties (package_type, size, package_duration, destination) are resolved automatically from the referenced package.

When package_id is sent with package_type: The explicit fields from the request body are used instead of the package's own properties.

When only package_type is sent: The conditional field requirements apply based on the type.

Package type requirements:

  • data-limitedsize: required, package_duration: not required
  • time-limited (deprecated — use data-limited with a package_duration)size: required, package_duration: required
  • unlimitedsize: not required, package_duration: required
  • startersize: required, package_duration: required
package_specification.​package_idstring

ID of an existing package. At least one of package_id or package_type must be provided.

When package_id is provided without package_type: The package's properties (package_type, size, package_duration, destination) are resolved automatically from the referenced package.

When package_id is provided with package_type: The explicit fields from the request body are used instead of the package's own properties.

package_specification.​package_typestring

Type of package. At least one of package_id or package_type must be provided.

  • starter: Small data allowance for short duration, ideal for trials
  • data-limited: Traditional packages with specific data allowance
  • unlimited: Unrestricted data usage with fair use policy
  • time-limited (deprecated — use data-limited with a package_duration): Fixed data allowance with time constraint for full-speed access
Enum"starter""data-limited""unlimited""time-limited"
package_specification.​sizestring^[0-9]\d*(\.\d+)?(MB|GB)$

Package size (e.g. "1GB", "5GB"). Required when package_type is data-limited, time-limited, or starter. Not required for unlimited.

package_specification.​package_durationinteger>= 1

Duration of the package in days. Required when package_type is time-limited, unlimited, or starter. Not required for data-limited.

package_specification.​destinationstring or Array of strings

Country ISO3 code or IATA code, or an array for multi-destination. Optional — omit for destination-agnostic promo codes.

One of:

Country ISO3 code or IATA code, or an array for multi-destination. Optional — omit for destination-agnostic promo codes.

string^[A-Z]{2,3}$

Country ISO3 code or IATA code, or an array for multi-destination. Optional — omit for destination-agnostic promo codes.

curl -i -X PUT \
  'https://docs.hubbyesim.com/_mock/apis/openapi/bookings/{booking_id}/updatePackageSpecification/{uuid}' \
  -H 'Content-Type: application/json' \
  -d '{
    "package_specification": {
      "package_id": "string",
      "package_type": "starter",
      "size": "string",
      "package_duration": 1,
      "destination": "ESP"
    }
  }'

Responses

Successfully updated the single promo code. updated_count is always 1; there is no skipped_count.

Bodyapplication/json
successboolean
Example: true
messagestring
Example: "Successfully updated package specification for booking."
dataobject(UpdatePackageSpecForPromoCodesResponse)
Response
application/json
{ "success": true, "message": "Successfully updated package specification for booking.", "data": { "external_id": "string", "id": "string", "title": "string", "first_name": "string", "last_name": "string", "full_name": "string", "pax": 0, "email": "user@example.com", "phone": "string", "booking_id": "string", "flight_number": "string", "partner": "string", "status": "PENDING", "brand": "string", "return_date": "2019-08-24T14:15:22Z", "departure_date": "2019-08-24T14:15:22Z", "gender": "M", "locale": "string", "promo_codes": [], "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "created_by": "string", "updated_by": "string", "custom_branding": "string", "departure_location": "string", "package_queues": [], "updated_count": 0, "skipped_count": 0 } }

Destination

Operations

PromoCode

Operations

eSIM

Operations

WebView

Operations

Native eSIM Integration

Operations

Webhooks

Webhooks