Hubby API (2.0.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, time-limited)

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: Packages that provide a fixed data allowance for a specific duration with full-speed access. These packages expire when either the data limit or time limit is reached.

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 Example: "1678901234GET/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";
const baseUrl = "YOUR_BASE_URL";

// Function to generate headers for API request
function generateApiHeaders(method, path) {
    //Timestamp is in milliseconds e.g. 1715558400000
    const timestamp = Math.floor(Date.now()).toString();

    // Ensure url is a string
    let path = String(url);

    // Remove baseUrl from the url if present
    path = processedUrl.replace(baseUrl, '');

    // Create query string if needed
    const queryString = new URL(url).search;
    if (queryString) {
        processedUrl += queryString;
    }

    // Validate public key
    if (!publicKey) {
        throw new Error("Public key is required");
    }

    // Create the payload
    // Sample payload: 1715558400000GET/bookings?bookingId=1234567890
    const payload = timestamp + method + path;

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

    // Return headers object
    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/v2_0_0/openapi/
Production server
https://api.hubbyesim.com/api/
Staging server
https://api-staging.hubby.dev/api/

Booking

Operations

Package

Operations

Country

Operations

PromoCode

Operations

eSIM

Operations

WebView

Operations

Native API

Operations

Get user state

Request

Returns everything needed to render any screen in a native eSIM integration: eSIM status, installation credentials, and all packages with live usage data.

This is the central endpoint for the Native API. One call gives you the traveler's full state.

When to call:

  • Every time the traveler opens the eSIM section
  • After a top-up purchase (to confirm the new package appeared)
  • Periodically for data meter refresh (or rely on webhooks)

Authentication: HMAC-SHA256 (same as all partner endpoints).

Path
external_user_idstringrequired

The partner's own user identifier

Example: partner_user_456
curl -i -X GET \
  https://docs.hubbyesim.com/_mock/apis/v2_0_0/openapi/native/users/partner_user_456

Responses

User state retrieved successfully

Bodyapplication/json
successboolean
Example: true
dataobject
Response
application/json
{ "success": true, "data": { "external_user_id": "partner_user_456", "esim": {}, "packages": [] } }

Get installation instructions

Request

Returns AI-generated, device-specific eSIM installation instructions tailored to the traveler's exact device model, OS version, locale, and current eSIM state.

You do not need to hard-code or maintain installation content in your app — Hubby keeps instructions current for every device and OS version.

The response adapts to:

  • Device model and OS version — different steps for iOS 17.4+ (programmatic) vs older iOS (QR scan) vs Android (intent-based)
  • eSIM state — if already installed, instructions shift to activation/troubleshooting
  • Locale — instructions returned in the requested language

Authentication: HMAC-SHA256 (same as all partner endpoints).

Path
external_user_idstringrequired

The partner's own user identifier

Example: partner_user_456
Bodyapplication/jsonrequired
device_infoobjectrequired
device_info.​osstringrequired

Operating system

Example: "iOS"
device_info.​os_versionstringrequired

OS version

Example: "17.4"
device_info.​device_modelstringrequired

Device model

Example: "iPhone 15 Pro"
localestring

Language for the instructions (e.g., "en", "es", "de")

Example: "en"
curl -i -X POST \
  https://docs.hubbyesim.com/_mock/apis/v2_0_0/openapi/native/users/partner_user_456/instructions \
  -H 'Content-Type: application/json' \
  -d '{
    "device_info": {
      "os": "iOS",
      "os_version": "17.4",
      "device_model": "iPhone 15 Pro"
    },
    "locale": "en"
  }'

Responses

Installation instructions generated successfully

Bodyapplication/json
successboolean
Example: true
dataobject
Response
application/json
{ "success": true, "data": { "instructions": [], "device_summary": "iPhone 15 Pro running iOS 17.4", "install_method": "qr_code" } }

Activate a queued package

Request

Activates a queued package for a traveler. If there is a currently active package, it is terminated and the selected package becomes active.

When to call: Only when the traveler has multiple queued packages and needs to choose which one to activate. If only one package is queued, it auto-activates when the traveler connects to a local network at the destination.

Authentication: HMAC-SHA256 (same as all partner endpoints).

Path
package_idstringrequired

The package ID to activate (from the user state response)

Example: pkg_abc
curl -i -X POST \
  https://docs.hubbyesim.com/_mock/apis/v2_0_0/openapi/native/packages/pkg_abc/activate

Responses

Package activated successfully

Bodyapplication/json
successboolean
Example: true
dataobject
Response
application/json
{ "success": true, "data": { "package_id": "pkg_abc", "destination": "TR", "size": "1GB", "status": "active", "activated_at": "2026-07-20T10:00:00Z", "previous_package": {} } }

Webhooks

Webhooks