Starter Package
This guide demonstrates how to integrate Hubby eSIM API to create bookings with starter packages where the destination is specified upfront. This is ideal for integrations where you know the customer's travel destination at booking time.
Overview
In this integration:
- You create a booking with a starter package and a specific destination
- The system generates a promo code for that destination
- You provide the promo code to your customer
- The customer redeems the code in the Hubby app with the destination already set
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
x-api-key: Your public API keyx-timestamp: Current timestamp in milliseconds (Unix epoch time)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 include
/api(e.g.,/api/bookings). The route/bookingsdoes not exist on its own - 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/bookingsStaging (for testing):
POST https://api-staging.hubby.dev/api/bookingsRequest Body
For a starter package booking with a destination, you need:
{
"booking_id": "YOUR-BOOKING-123",
"departure_date": "2025-06-15T00:00:00.000Z",
"return_date": "2025-06-22T00:00:00.000Z",
"departure_location": "DMK",
"first_name": "John",
"communication_options": {
"should_send_message": true,
"channels": ["EMAIL"]
},
"package_specifications": [
{
"package_type": "starter",
"destination": "Thailand"
}
]
}Field Descriptions
booking_id(required ifemailis not provided): Any unique reference you want to store with us for this booking. This is not the customer-facing booking ID, but rather an internal identifier you can use to correlate bookings in your system with Hubby records. Eitherbooking_idoremailmust be provideddeparture_date(required): Travel start date in ISO 8601 format (e.g.,"2025-06-15T00:00:00.000Z")return_date(optional): Travel end date in ISO 8601 format (e.g.,"2025-06-22T00:00:00.000Z")departure_location(optional): The customer's departure airport code (IATA 3-letter code, e.g.,"AMS","JFK","LHR"). Useful for analytics and localized communicationsemail(required ifbooking_idis not provided): Customer email address. If provided, Hubby sends eSIM installation instructions directly to the customer. Eitheremailorbooking_idmust be provided; both can be suppliedfirst_name(optional): Customer first namelast_name(optional): Customer last namecommunication_options(optional): Controls whether Hubby sends emails to the customer. If not provided, messages will not be sentshould_send_message(boolean): Set totrueto allow Hubby to send emails, orfalseto handle communication yourselfchannels(array): List of communication channels to use. Available options:"EMAIL","SMS","WHATSAPP","PUSH"
package_specifications(required): Array with at least one package specification. Each entry is resolved bypackage_id(direct reference) or by other properties (destination, size, etc.); whenpackage_idis set, other fields in that entry are ignored.package_type: "starter": Creates a starter packagedestination(required): Country name or ISO 3166-1 alpha-2 country code (e.g.,"Thailand"or"TH")
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, // ISO 8601 format
return_date: bookingData.returnDate, // ISO 8601 format
departure_location: bookingData.departureLocation,
email: bookingData.email,
first_name: bookingData.firstName,
last_name: bookingData.lastName,
communication_options: {
should_send_message: true, // Set to false if you handle emails yourself
channels: ['EMAIL']
},
package_specifications: [
{
package_type: 'starter',
destination: bookingData.destination
}
]
};
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-15T00:00:00.000Z',
returnDate: '2025-06-22T00:00:00.000Z',
departureLocation: 'DMK',
email: 'customer@example.com',
firstName: 'John',
lastName: 'Doe',
destination: 'Thailand'
});
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);
console.log('Destination:', result.data.promo_codes[0].destination);
} 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, // ISO 8601 format
return_date: bookingData.returnDate, // ISO 8601 format
departure_location: bookingData.departureLocation,
email: bookingData.email,
first_name: bookingData.firstName,
last_name: bookingData.lastName,
communication_options: {
should_send_message: true, // Set to false if you handle emails yourself
channels: ['EMAIL']
},
package_specifications: [
{
package_type: 'starter',
destination: bookingData.destination
}
]
};
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-15T00:00:00.000Z',
returnDate: '2025-06-22T00:00:00.000Z',
departureLocation: 'DMK',
email: 'customer@example.com',
firstName: 'John',
lastName: 'Doe',
destination: 'Thailand'
})
.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);
console.log('Destination:', result.data.promo_codes[0].destination);
})
.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",
"return_date": "2025-06-22T00: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,
"destination": "TH"
}
]
}
}Using the Promo Code
You have two options for providing the promo code to your customer:
Option 1: Provide the Promo Code (Recommended)
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:
- Extract the
promo_codefrom the response:result.data.promo_codes[0].promo_code - Provide this code to your customer (via email, SMS, or in your booking confirmation)
- The customer enters the code in the Hubby app or on the Hubby website
- The destination is pre-selected based on the booking
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:
- Extract the
claim_urlfrom the response:result.data.promo_codes[0].claim_url - Include this URL in your booking confirmation email or SMS
- When customers click the link, they're taken to a page where they enter their email
- 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, invalid country code, 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
- Store credentials securely: Never hardcode API keys in your source code. Use environment variables or secure configuration management
- Handle errors gracefully: Implement proper error handling and retry logic for network issues
- Validate input: Validate customer data and country codes before sending to the API
- Include email addresses: Provide customer email addresses when possible so Hubby can send installation instructions automatically
- Store promo codes: Save the promo codes in your database for customer support purposes
- Test in development: Use test credentials and test bookings before going to production
- Use valid country codes: Ensure you're using valid ISO 3166-1 alpha-2 country codes for destinations
Next Steps
Support
For integration support or questions:
- Email: tech@hubbyesim.com
- Check the API reference for complete endpoint documentation