Skip to main content

Quick Reference

Event TypeTriggerKey Data
esim.createdeSIM purchase complete, data readyFull eSIM details with QR code
esim.activatedeSIM activated on deviceStatus change, activation time
esim.data_updatedData usage changesUsage amounts, remaining data
esim.usage_updatedUsage requeriedFull usage statistics
esim.suspendedeSIM suspendedReason, previous status
esim.expiredeSIM validity expiredExpiration details
esim.status_changedAny other status changeBefore/after status
esim.requeriedManual data refreshUpdated eSIM data

Common Payload Structure

All webhooks share this base structure:
{
  "event": "string",        // Event type
  "id": "string",           // Unique event ID
  "timestamp": "string",    // ISO 8601 timestamp
  "data": {                 // eSIM object (structure below)
    // ... eSIM data ...
  },
  "previousStatus": "string",  // Optional: for status changes
  "newStatus": "string",       // Optional: for status changes
  "changes": {                 // Optional: changed fields
    "fieldName": {
      "old": "value",
      "new": "value"
    }
  }
}

eSIM Data Object (Common to All Events)

{
  // Identifiers
  "id": "507f1f77bcf86cd799439011",
  "transactionId": "esim_1762811805580_83",
  "orderNo": "ORD2024110001",
  "packageCode": "US-1GB-7D",
  
  // eSIM Credentials
  "iccid": "8901234567890123456",
  "imsi": "123456789012345",
  "msisdn": "+1234567890",
  "ac": "LPA:1$smdp.example.com$ABC-DEF-123",
  "eid": "89012345678901234567890123456789012",
  
  // Activation
  "qrCodeUrl": "https://api.vellosim.com/esim/qr/123.png",
  "shortUrl": "https://esim.vls.io/abc123",
  
  // Status
  "esimStatus": "CREATED | ACTIVATED | SUSPENDED | EXPIRED | etc.",
  "smdpStatus": "RELEASED | DOWNLOADED | INSTALLED | etc.",
  
  // Data & Validity
  "totalVolume": 1024,              // MB
  "totalDuration": 7,               // days
  "dataUsage": 512.5,              // MB used
  "data_usage_remain": 511.5,      // MB remaining
  "totalData": 1024,               // MB total allowance
  "validity_usage_remain": 5,      // days remaining
  
  // Timestamps
  "activateTime": "2024-11-10T12:00:00.000Z",
  "expiredTime": "2024-11-17T12:00:00.000Z",
  "lastUsageUpdate": "2024-11-10T15:30:00.000Z",
  "createdAt": "2024-11-10T10:00:00.000Z",
  "updatedAt": "2024-11-10T15:30:00.000Z",
  
  // Package Info
  "packageDetails": {
    "name": "USA 1GB - 7 Days",
    "code": "US-1GB-7D",
    "slug": "usa-1gb-7days",
    "volume": 1024,
    "duration": 7,
    "location": "United States",
    "locationLogo": "https://cdn.vellosim.com/flags/us.png",
    "price": 9.99,
    "currency": "USD",
    "description": "1GB data for USA, valid for 7 days",
    "speed": "4G LTE"
  }
}

Event Type Details

1. esim.created

When: eSIM purchase completed and resource details retrieved from provider Timing: 5-30 seconds after purchase Full Example:
{
  "event": "esim.created",
  "id": "1699625400123_abc",
  "timestamp": "2024-11-10T15:30:00.000Z",
  "data": {
    "id": "507f1f77bcf86cd799439011",
    "transactionId": "esim_1762811805580_83",
    "orderNo": "ORD2024110001",
    "packageCode": "US-1GB-7D",
    "iccid": "8901234567890123456",
    "imsi": "123456789012345",
    "msisdn": "+12025550123",
    "ac": "LPA:1$smdp.provider.com$ACTIVATION123",
    "qrCodeUrl": "https://api.vellosim.com/esim/qr/507f1f77bcf86cd799439011.png",
    "shortUrl": "https://esim.vls.io/abc123",
    "esimStatus": "CREATED",
    "smdpStatus": "RELEASED",
    "eid": null,
    "totalVolume": 1024,
    "totalDuration": 7,
    "dataUsage": 0,
    "data_usage_remain": 1024,
    "totalData": 1024,
    "validity_usage_remain": 7,
    "activateTime": null,
    "expiredTime": null,
    "lastUsageUpdate": null,
    "packageDetails": {
      "name": "USA 1GB - 7 Days",
      "code": "US-1GB-7D",
      "volume": 1024,
      "duration": 7,
      "location": "United States",
      "locationLogo": "https://cdn.vellosim.com/flags/us.png",
      "price": 9.99,
      "currency": "USD",
      "description": "Stay connected in the USA with 1GB of high-speed data",
      "speed": "4G LTE"
    },
    "createdAt": "2024-11-10T15:29:45.000Z",
    "updatedAt": "2024-11-10T15:30:00.000Z"
  }
}
Use Cases:
  • Update your database with eSIM details
  • Send confirmation email with QR code
  • Display eSIM in user’s dashboard
  • Generate invoice/receipt

2. esim.activated

When: User installs and activates the eSIM profile on their device Timing: When provider notifies activation (typically within minutes of installation) Full Example:
{
  "event": "esim.activated",
  "id": "1699628000456_def",
  "timestamp": "2024-11-10T16:00:00.000Z",
  "data": {
    "id": "507f1f77bcf86cd799439011",
    "transactionId": "esim_1762811805580_83",
    "orderNo": "ORD2024110001",
    "packageCode": "US-1GB-7D",
    "iccid": "8901234567890123456",
    "imsi": "123456789012345",
    "msisdn": "+12025550123",
    "esimStatus": "ACTIVATED",
    "smdpStatus": "INSTALLED",
    "eid": "89012345678901234567890123456789012",
    "totalVolume": 1024,
    "totalDuration": 7,
    "dataUsage": 0,
    "data_usage_remain": 1024,
    "validity_usage_remain": 7,
    "activateTime": "2024-11-10T16:00:00.000Z",
    "expiredTime": "2024-11-17T16:00:00.000Z",
    "lastUsageUpdate": "2024-11-10T16:00:00.000Z",
    "createdAt": "2024-11-10T15:29:45.000Z",
    "updatedAt": "2024-11-10T16:00:00.000Z"
  },
  "previousStatus": "CREATED",
  "newStatus": "ACTIVATED"
}
Key Fields:
  • activateTime: When activation occurred
  • expiredTime: Calculated expiration date
  • eid: Device identifier
  • previousStatus: Status before activation
  • newStatus: “ACTIVATED” or “IN_USE”
Use Cases:
  • Start usage tracking
  • Send activation confirmation
  • Update analytics/reports
  • Trigger welcome message

3. esim.data_updated

When: Data usage information changes (every few hours or at usage milestones) Timing: Varies (typically when 25%, 50%, 75%, 90%, 100% thresholds reached) Full Example:
{
  "event": "esim.data_updated",
  "id": "1699641600789_ghi",
  "timestamp": "2024-11-11T09:30:00.000Z",
  "data": {
    "id": "507f1f77bcf86cd799439011",
    "transactionId": "esim_1762811805580_83",
    "iccid": "8901234567890123456",
    "esimStatus": "ACTIVATED",
    "totalVolume": 1024,
    "totalData": 1024,
    "dataUsage": 512.5,
    "data_usage_remain": 511.5,
    "lastUsageUpdate": "2024-11-11T09:30:00.000Z",
    "updatedAt": "2024-11-11T09:30:00.000Z"
  },
  "changes": {
    "dataUsage": {
      "old": 450.2,
      "new": 512.5
    },
    "data_usage_remain": {
      "old": 573.8,
      "new": 511.5
    }
  }
}
Key Fields:
  • dataUsage: Current usage in MB
  • data_usage_remain: Remaining data in MB
  • changes: Shows what changed from previous state
Use Cases:
  • Track consumption patterns
  • Send low data alerts
  • Offer top-up/add-ons
  • Update usage charts

4. esim.usage_updated

When: Complete usage information refreshed (data + validity) Timing: Periodic updates or after manual requery Full Example:
{
  "event": "esim.usage_updated",
  "id": "1699655200321_jkl",
  "timestamp": "2024-11-11T13:00:00.000Z",
  "data": {
    "id": "507f1f77bcf86cd799439011",
    "transactionId": "esim_1762811805580_83",
    "iccid": "8901234567890123456",
    "esimStatus": "ACTIVATED",
    "totalVolume": 1024,
    "totalDuration": 7,
    "dataUsage": 768.3,
    "data_usage_remain": 255.7,
    "validity_usage_remain": 4,
    "activateTime": "2024-11-10T16:00:00.000Z",
    "expiredTime": "2024-11-17T16:00:00.000Z",
    "lastUsageUpdate": "2024-11-11T13:00:00.000Z",
    "updatedAt": "2024-11-11T13:00:00.000Z"
  },
  "changes": {
    "dataUsage": {
      "old": 512.5,
      "new": 768.3
    },
    "data_usage_remain": {
      "old": 511.5,
      "new": 255.7
    },
    "validity_usage_remain": {
      "old": 5,
      "new": 4
    }
  }
}
Key Fields:
  • dataUsage + data_usage_remain: Data usage
  • validity_usage_remain: Days remaining
  • lastUsageUpdate: Timestamp of update
Use Cases:
  • Comprehensive usage sync
  • Dashboard updates
  • Analytics refresh
  • Billing updates

5. esim.suspended

When: eSIM is suspended (manual or automatic) Timing: Immediately when suspension occurs Full Example:
{
  "event": "esim.suspended",
  "id": "1699714800999_mno",
  "timestamp": "2024-11-11T16:30:00.000Z",
  "data": {
    "id": "507f1f77bcf86cd799439011",
    "transactionId": "esim_1762811805580_83",
    "iccid": "8901234567890123456",
    "esimStatus": "REVOKED",
    "dataUsage": 850.0,
    "data_usage_remain": 174.0,
    "updatedAt": "2024-11-11T16:30:00.000Z"
  },
  "previousStatus": "ACTIVATED",
  "newStatus": "REVOKED"
}
Possible Status Values:
  • REVOKED: Profile revoked
  • CANCEL: Order cancelled
Use Cases:
  • Notify user of suspension
  • Display suspension reason
  • Offer reactivation options
  • Update access controls

6. esim.expired

When: eSIM reaches end of validity period or data exhausted Timing: At expiration time or when data runs out Full Example:
{
  "event": "esim.expired",
  "id": "1699801200123_pqr",
  "timestamp": "2024-11-17T16:00:00.000Z",
  "data": {
    "id": "507f1f77bcf86cd799439011",
    "transactionId": "esim_1762811805580_83",
    "iccid": "8901234567890123456",
    "esimStatus": "USED_EXPIRED",
    "dataUsage": 980.5,
    "data_usage_remain": 43.5,
    "validity_usage_remain": 0,
    "activateTime": "2024-11-10T16:00:00.000Z",
    "expiredTime": "2024-11-17T16:00:00.000Z",
    "updatedAt": "2024-11-17T16:00:00.000Z"
  },
  "previousStatus": "ACTIVATED",
  "newStatus": "USED_EXPIRED"
}
Possible Status Values:
  • USED_UP: Data fully consumed
  • USED_EXPIRED: Data used + validity expired
  • UNUSED_EXPIRED: Expired with unused data
Use Cases:
  • Archive eSIM data
  • Offer renewal/repurchase
  • Generate usage report
  • Update analytics

7. esim.status_changed

When: Any status change not covered by specific events Timing: Varies based on status change Full Example:
{
  "event": "esim.status_changed",
  "id": "1699728000321_stu",
  "timestamp": "2024-11-11T19:46:40.000Z",
  "data": {
    "id": "507f1f77bcf86cd799439011",
    "transactionId": "esim_1762811805580_83",
    "iccid": "8901234567890123456",
    "esimStatus": "USED_UP",
    "dataUsage": 1024,
    "data_usage_remain": 0,
    "validity_usage_remain": 3,
    "updatedAt": "2024-11-11T19:46:40.000Z"
  },
  "previousStatus": "ACTIVATED",
  "newStatus": "USED_UP"
}
Use Cases:
  • Catch all status changes
  • Handle edge cases
  • Maintain audit trail
  • Debug status transitions

8. esim.requeried

When: eSIM data manually refreshed from provider Timing: After admin or system requery operation Full Example:
{
  "event": "esim.requeried",
  "id": "1699732800456_vwx",
  "timestamp": "2024-11-11T21:00:00.000Z",
  "data": {
    "id": "507f1f77bcf86cd799439011",
    "transactionId": "esim_1762811805580_83",
    "orderNo": "ORD2024110001",
    "iccid": "8901234567890123456",
    "esimStatus": "ACTIVATED",
    "dataUsage": 892.7,
    "data_usage_remain": 131.3,
    "validity_usage_remain": 3,
    "lastUsageUpdate": "2024-11-11T21:00:00.000Z",
    "updatedAt": "2024-11-11T21:00:00.000Z"
  }
}
Use Cases:
  • Sync latest provider data
  • Resolve data discrepancies
  • Force update in UI
  • Debugging/support

eSIM Status Values

StatusDescriptionTriggers Event
PENDINGOrder placed, awaiting provider-
CREATEDeSIM created, ready for activationesim.created
ACTIVATEDActivated on deviceesim.activated
IN_USECurrently in use (same as ACTIVATED)esim.activated
SUSPENDEDTemporarily suspendedesim.suspended
EXPIREDValidity expiredesim.expired
USED_UPData fully consumedesim.expired
USED_EXPIREDData used + validity expiredesim.expired
UNUSED_EXPIREDExpired with unused dataesim.expired
CANCELOrder cancelledesim.suspended
REVOKEDProfile revokedesim.suspended
FAILEDOrder failedesim.status_changed

SMDP Status Values

StatusDescription
RELEASEDProfile released, ready for download
DOWNLOADEDProfile downloaded to device
INSTALLEDProfile installed on device
ENABLEDProfile enabled for use
DISABLEDProfile disabled
DELETEDProfile deleted from device

Data Units

All data-related fields use megabytes (MB) as the unit:
{
  "totalVolume": 1024,        // 1024 MB = 1 GB
  "dataUsage": 512.5,         // 512.5 MB used
  "data_usage_remain": 511.5, // 511.5 MB remaining
  "totalData": 1024           // 1024 MB total allowance
}

Time/Duration Units

Validity duration typically in days:
{
  "totalDuration": 7,          // 7 days validity
  "validity_usage_remain": 5   // 5 days remaining
}

Timestamp Format

All timestamps are in ISO 8601 format (UTC):
{
  "timestamp": "2024-11-10T15:30:00.000Z",
  "activateTime": "2024-11-10T16:00:00.000Z",
  "expiredTime": "2024-11-17T16:00:00.000Z"
}

Webhook Headers

Every webhook request includes these headers:
Content-Type: application/json
X-Webhook-Signature: <hmac_sha256_signature>
X-Webhook-Event: <event_type>
X-Webhook-Id: <unique_event_id>
X-Webhook-Timestamp: <iso8601_timestamp>
User-Agent: Vellosim-Webhooks/1.0

Signature Verification

Verify webhook signatures to ensure requests are from Vellosim:
const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(payload))
    .digest('hex');
  
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

// Express.js example
app.post('/webhooks/vellosim', express.json(), (req, res) => {
  const signature = req.headers['x-webhook-signature'];
  const webhookSecret = process.env.VELLOSIM_WEBHOOK_SECRET;
  
  if (!verifyWebhookSignature(req.body, signature, webhookSecret)) {
    return res.status(401).send('Invalid signature');
  }
  
  const event = req.body;
  
  // Process the webhook event
  handleWebhookEvent(event);
  
  res.status(200).send('Webhook received');
});

Handling Webhook Events

Process different event types appropriately:
function handleWebhookEvent(event) {
  // Store event ID to prevent duplicate processing
  if (isEventProcessed(event.id)) {
    console.log(`Event ${event.id} already processed`);
    return;
  }
  
  switch (event.event) {
    case 'esim.created':
      handleEsimCreated(event.data);
      break;
      
    case 'esim.activated':
      handleEsimActivated(event.data, event.previousStatus);
      break;
      
    case 'esim.data_updated':
      handleDataUpdated(event.data, event.changes);
      break;
      
    case 'esim.usage_updated':
      handleUsageUpdated(event.data, event.changes);
      break;
      
    case 'esim.suspended':
      handleEsimSuspended(event.data, event.previousStatus);
      break;
      
    case 'esim.expired':
      handleEsimExpired(event.data);
      break;
      
    case 'esim.status_changed':
      handleStatusChanged(event.data, event.previousStatus, event.newStatus);
      break;
      
    case 'esim.requeried':
      handleEsimRequeried(event.data);
      break;
      
    default:
      console.log(`Unhandled event type: ${event.event}`);
  }
  
  // Mark event as processed
  markEventProcessed(event.id);
}

function handleEsimCreated(data) {
  // Update database with eSIM details
  console.log(`New eSIM created: ${data.transactionId}`);
  
  // Send confirmation email with QR code
  sendEsimConfirmation(data.customerEmail, {
    qrCodeUrl: data.qrCodeUrl,
    shortUrl: data.shortUrl,
    ac: data.ac,
    packageDetails: data.packageDetails
  });
  
  // Update order status
  updateOrderStatus(data.id, 'CREATED');
}

function handleEsimActivated(data, previousStatus) {
  // Notify customer that eSIM is active
  console.log(`eSIM activated: ${data.iccid}`);
  
  sendActivationNotification(data.customerEmail, {
    iccid: data.iccid,
    activateTime: data.activateTime,
    expiredTime: data.expiredTime
  });
  
  // Update status and start tracking
  updateOrderStatus(data.id, 'ACTIVATED');
  startUsageTracking(data.id);
}

function handleDataUpdated(data, changes) {
  // Update usage in database
  updateUsageStats(data.id, {
    dataUsage: data.dataUsage,
    data_usage_remain: data.data_usage_remain
  });
  
  // Check for low data threshold (e.g., 10% remaining)
  const usagePercent = (data.dataUsage / data.totalData) * 100;
  if (usagePercent >= 90 && usagePercent < 100) {
    sendLowDataAlert(data.customerEmail, data);
  }
}

Webhook Response Best Practices

Always return a 200 status code to acknowledge receipt. Process the webhook asynchronously if needed.
Your endpoint should respond within 5 seconds. For longer processing, queue the webhook for async processing.
Webhooks may be sent multiple times. Use the event ID to prevent duplicate processing.
Always verify webhook signatures to ensure authenticity and prevent spoofing.

Error Handling & Retry Logic

Retry Schedule

Failed webhooks are retried with exponential backoff:
  • Attempt 1: Immediate
  • Attempt 2: 1 minute later
  • Attempt 3: 5 minutes later
  • Attempt 4: 15 minutes later
  • Attempt 5: 1 hour later
  • Attempt 6: 2 hours later
Maximum: 5 retries (6 total attempts)

HTTP Status Codes

  • 2xx: Success, no retry
  • 410 Gone: Endpoint invalid, stop retrying
  • All others: Retry with backoff

Async Processing Example

const Queue = require('bull');
const webhookQueue = new Queue('webhook-processing');

// Webhook endpoint - responds immediately
app.post('/webhooks/vellosim', express.json(), async (req, res) => {
  const signature = req.headers['x-webhook-signature'];
  
  if (!verifyWebhookSignature(req.body, signature, WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }
  
  // Add to queue for async processing
  await webhookQueue.add({
    event: req.body,
    receivedAt: new Date()
  });
  
  // Respond immediately
  res.status(200).send('Webhook queued');
});

// Process webhooks asynchronously
webhookQueue.process(async (job) => {
  const { event } = job.data;
  await handleWebhookEvent(event);
});

Testing Webhooks

Local Testing with ngrok

Expose your local server for webhook testing:
# Start your local server
npm start

# In another terminal, start ngrok
ngrok http 3000

# Use the ngrok URL as your webhook endpoint
# Example: https://abc123.ngrok-free.app/webhooks/vellosim

Test with webhook.site

Use webhook.site to inspect webhook payloads:
  1. Go to webhook.site
  2. Copy the unique URL
  3. Configure it as your webhook endpoint
  4. Trigger events and inspect payloads in real-time

Integration Checklist

✅ Webhook URL configured in API key settings
✅ Signature verification implemented
✅ Idempotency handling (using event.id)
✅ Async processing (return 200 OK immediately)
✅ Error logging and monitoring
✅ Retry handling on your side (optional)
✅ Test with ngrok/webhook.site
✅ Monitor webhook delivery logs

Next Steps