Last updated

Destinationless

This guide demonstrates how to integrate Hubby eSIM API to create bookings with destinationless starter packages. This is perfect for simple integrations where customers can choose their destination later.

Overview

In this integration:

  1. You create a booking with a destinationless starter package
  2. The system generates a promo code
  3. You provide the promo code to your customer
  4. The customer redeems the code in the Hubby app and selects their destination

Prerequisites

  • A Hubby Partner account with API access
  • Your API credentials (API key and secret key)
  • Ability to make HTTP POST requests from your system

API Endpoints

Hubby provides two API endpoints for different environments:

Production Environment

  • Base URL: https://api.hubbyesim.com
  • Use for: Live applications and production integrations
  • Full endpoint: https://api.hubbyesim.com/api/bookings

Staging Environment

  • Base URL: https://api-staging.hubby.dev
  • Use for: Testing, development, and integration testing
  • Full endpoint: https://api-staging.hubby.dev/api/bookings

When to Use Each Environment

Use Staging (api-staging.hubby.dev) when:

  • Developing and testing your integration
  • Verifying authentication and request formats
  • Testing booking creation workflows
  • Debugging integration issues

Use Production (api.hubbyesim.com) when:

  • Your integration is complete and tested
  • You're ready to create real bookings for customers
  • You're running in a live/production environment

Important Notes:

  • Both environments use the same authentication method
  • Each environment has its own API credentials (staging and production keys are separate)
  • Data created in staging is separate from production
  • Always test thoroughly in staging before using production

Authentication

Hubby API uses HMAC-SHA256 authentication. Every request must include three headers:

Required Headers

  1. x-api-key: Your public API key
  2. x-timestamp: Current timestamp in milliseconds (Unix epoch time)
  3. x-signature: HMAC-SHA256 signature of the request

How Authentication Works

The signature is computed by creating an HMAC-SHA256 hash of a message string using your secret key. The message format is:

{timestamp}{HTTP_METHOD}/api{path}

For example, for a POST request to /api/bookings:

  • If timestamp is 1704067200000
  • The message would be: 1704067200000POST/api/bookings

Authentication Implementation

Here's how to generate the authentication headers:

const crypto = require('crypto');

function generateAuthHeaders(method, path, apiKey, apiSecret) {
  // Get current timestamp in milliseconds
  const timestamp = Date.now().toString();
  
  // Create the message to sign
  const message = `${timestamp}${method}/api${path}`;
  
  // Generate HMAC-SHA256 signature
  const signature = crypto
    .createHmac('sha256', apiSecret)
    .update(message)
    .digest('hex');
  
  // Return headers
  return {
    'x-api-key': apiKey,
    'x-timestamp': timestamp,
    'x-signature': signature,
    'Content-Type': 'application/json'
  };
}

Important Authentication Notes

  • Timestamp must be current: The timestamp must be within 5 minutes of the server time
  • Use exact path: The path in the signature must match the actual API path (e.g., /bookings not /api/bookings in the message)
  • Method must be uppercase: Use GET, POST, PUT, DELETE (uppercase)
  • Keep your secret secure: Never expose your API secret key in client-side code or public repositories

Creating a Booking

Endpoint

Production:

POST https://api.hubbyesim.com/api/bookings

Staging (for testing):

POST https://api-staging.hubby.dev/api/bookings

Request Body

For a destinationless starter package booking, you need:

{
  "booking_id": "YOUR-BOOKING-123",
  "departure_date": "2025-06-15",
  "first_name": "John",
  "last_name": "Doe",
  "package_specifications": [
    {
      "package_type": "starter"
    }
  ]
}

Field Descriptions

  • booking_id (optional but recommended): Your internal booking reference
  • departure_date (required): Travel date in YYYY-MM-DD format
  • email (optional): Customer email address. If provided, Hubby can send eSIM installation instructions directly to the customer
  • first_name (optional): Customer first name
  • last_name (optional): Customer last name
  • package_specifications (required): Array with at least one package specification (package_type: 'starter' suffices for simplicity). Each entry is resolved by package_id (direct reference) or by other properties; when package_id is set, other fields in that entry are ignored.
  • package_type: "starter": Creates a destinationless starter package (customer selects destination later)

Complete Example

Here's a complete example using Node.js:

const crypto = require('crypto');
const https = require('https');

// Your API credentials (store these securely!)
const API_KEY = 'your-api-key-here';
const API_SECRET = 'your-api-secret-here';
// Use staging for development, production for live
const API_BASE_URL = process.env.NODE_ENV === 'production' 
  ? 'https://api.hubbyesim.com'
  : 'https://api-staging.hubby.dev';

function generateAuthHeaders(method, path, apiKey, apiSecret) {
  const timestamp = Date.now().toString();
  const message = `${timestamp}${method}/api${path}`;
  const signature = crypto
    .createHmac('sha256', apiSecret)
    .update(message)
    .digest('hex');
  
  return {
    'x-api-key': apiKey,
    'x-timestamp': timestamp,
    'x-signature': signature,
    'Content-Type': 'application/json'
  };
}

async function createBooking(bookingData) {
  const path = '/bookings';
  const url = `${API_BASE_URL}/api${path}`;
  
  const headers = generateAuthHeaders('POST', path, API_KEY, API_SECRET);
  
  const requestBody = {
    booking_id: bookingData.bookingId,
    departure_date: bookingData.departureDate,
    email: bookingData.email,
    first_name: bookingData.firstName,
    last_name: bookingData.lastName,
    package_specifications: [
      {
        package_type: 'starter'
      }
    ]
  };
  
  return new Promise((resolve, reject) => {
    const urlObj = new URL(url);
    const options = {
      hostname: urlObj.hostname,
      port: urlObj.port || 443,
      path: urlObj.pathname,
      method: 'POST',
      headers: headers
    };
    
    const req = https.request(options, (res) => {
      let data = '';
      
      res.on('data', (chunk) => {
        data += chunk;
      });
      
      res.on('end', () => {
        try {
          const response = JSON.parse(data);
          if (res.statusCode === 200 || res.statusCode === 201) {
            resolve(response);
          } else {
            reject(new Error(`API Error: ${res.statusCode} - ${data}`));
          }
        } catch (e) {
          reject(new Error(`Failed to parse response: ${e.message}`));
        }
      });
    });
    
    req.on('error', (error) => {
      reject(error);
    });
    
    req.write(JSON.stringify(requestBody));
    req.end();
  });
}

// Example usage
async function main() {
  try {
    const result = await createBooking({
      bookingId: 'BOOKING-12345',
      departureDate: '2025-06-15',
      email: 'customer@example.com',
      firstName: 'John',
      lastName: 'Doe'
    });
    
    console.log('Booking created successfully!');
    console.log('Booking ID:', result.data.id);
    console.log('Promo Code:', result.data.promo_codes[0].promo_code);
    console.log('Claim URL:', result.data.promo_codes[0].claim_url);
  } catch (error) {
    console.error('Error creating booking:', error.message);
  }
}

main();

Using Fetch (Modern JavaScript)

const crypto = require('crypto');

const API_KEY = 'your-api-key-here';
const API_SECRET = 'your-api-secret-here';
// Use staging for development, production for live
const API_BASE_URL = process.env.NODE_ENV === 'production' 
  ? 'https://api.hubbyesim.com'
  : 'https://api-staging.hubby.dev';

function generateAuthHeaders(method, path, apiKey, apiSecret) {
  const timestamp = Date.now().toString();
  const message = `${timestamp}${method}/api${path}`;
  const signature = crypto
    .createHmac('sha256', apiSecret)
    .update(message)
    .digest('hex');
  
  return {
    'x-api-key': apiKey,
    'x-timestamp': timestamp,
    'x-signature': signature,
    'Content-Type': 'application/json'
  };
}

async function createBooking(bookingData) {
  const path = '/bookings';
  const url = `${API_BASE_URL}/api${path}`;
  const headers = generateAuthHeaders('POST', path, API_KEY, API_SECRET);
  
  const requestBody = {
    booking_id: bookingData.bookingId,
    departure_date: bookingData.departureDate,
    email: bookingData.email,
    first_name: bookingData.firstName,
    last_name: bookingData.lastName,
    package_specifications: [
      {
        package_type: 'starter'
      }
    ]
  };
  
  const response = await fetch(url, {
    method: 'POST',
    headers: headers,
    body: JSON.stringify(requestBody)
  });
  
  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`API Error: ${response.status} - ${errorText}`);
  }
  
  return await response.json();
}

// Example usage
createBooking({
  bookingId: 'BOOKING-12345',
  departureDate: '2025-06-15',
  email: 'customer@example.com',
  firstName: 'John',
  lastName: 'Doe'
})
  .then(result => {
    console.log('Booking created!');
    console.log('Promo Code:', result.data.promo_codes[0].promo_code);
    console.log('Claim URL:', result.data.promo_codes[0].claim_url);
  })
  .catch(error => {
    console.error('Error:', error.message);
  });

Response

A successful booking creation returns:

{
  "success": true,
  "message": "Booking created successfully.",
  "data": {
    "id": "xs8gWi1GXHWa7YyV4HVG",
    "booking_id": "YOUR-BOOKING-123",
    "first_name": "John",
    "last_name": "Doe",
    "departure_date": "2025-06-15T00:00:00.000Z",
    "promo_codes": [
      {
        "promo_code": "CXZ6ZM9NGA",
        "uuid": "5163cbc9-a96a-4a97-8577-bd3416bd3e4b",
        "claim_url": "https://platform.hubbyesim.com/claim-promocode/acme?uuid=51631111-1111-1aa1-1ee6-bd3416bd3e4b",
        "package_type": "starter",
        "package_size": "1GB",
        "package_duration": 365
      }
    ]
  }
}

Using the Promo Code

You have two options for providing the promo code to your customer:

When you include an email address in the booking, Hubby automatically sends the promo code with installation instructions to the customer. You can also provide the promo code directly:

  1. Extract the promo_code from the response: result.data.promo_codes[0].promo_code
  2. Provide this code to your customer (via email, SMS, or in your booking confirmation)
  3. The customer enters the code in the Hubby app or on the Hubby website
  4. The customer selects their destination when redeeming

Example:

const promoCode = result.data.promo_codes[0].promo_code;
// Store or display: "Your eSIM promo code: [promo_code]"

Option 2: Using the Claim URL

If you are not providing an email address in the booking, you can use the claim_url to collect the customer's email and deliver the promo code:

  1. Extract the claim_url from the response: result.data.promo_codes[0].claim_url
  2. Include this URL in your booking confirmation email or SMS
  3. When customers click the link, they're taken to a page where they enter their email
  4. Hubby then sends them the promo code with installation instructions

Example:

const claimUrl = result.data.promo_codes[0].claim_url;
// Include in confirmation: "Click here to get your eSIM: [claim_url]"

Error Handling

Common error responses:

  • 401 Unauthorized: Invalid or missing authentication headers
  • 400 Bad Request: Invalid request data (missing required fields, invalid date format, etc.)
  • 500 Internal Server Error: Server-side error

Always check the response status and handle errors appropriately:

if (!response.ok) {
  const error = await response.json();
  console.error('API Error:', error);
  // Handle error appropriately
}

Best Practices

  1. Store credentials securely: Never hardcode API keys in your source code. Use environment variables or secure configuration management
  2. Handle errors gracefully: Implement proper error handling and retry logic for network issues
  3. Validate input: Validate customer data before sending to the API
  4. Include email addresses: Provide customer email addresses when possible so Hubby can send installation instructions automatically
  5. Store promo codes: Save the promo codes in your database for customer support purposes
  6. Test in development: Use test credentials and test bookings before going to production

Next Steps

  1. Set up your API credentials
  2. Learn about other package types
  3. Implement error handling

Support

For integration support or questions:

  • Email: tech@hubbyesim.com
  • Check the API reference for complete endpoint documentation