SIM Card Provisioning
OmniCRM provides comprehensive support for provisioning both physical SIM cards and eSIMs (embedded SIMs) for mobile network operators and MVNOs. The system handles the complete lifecycle from inventory management through activation, assignment, and deprovisioning.
See also: Provisioning System <concepts_provisioning> for general provisioning concepts, Inventory <administration_inventory> for inventory management, Ansible Playbooks <concepts_ansible> for provisioning automation.
Overview
SIM provisioning in OmniCRM involves several integrated systems working together:
- Inventory Management - Tracks available SIM cards (physical and eSIM profiles)
- HSS/IMS Integration - Provisions subscriber credentials and voice services
- OCS Integration - Sets up billing and charging for the service
- Ansible Automation - Orchestrates the provisioning workflow
- Self-Signup Support - Enables customers to activate their own SIMs
Physical SIM Card Provisioning
Physical SIM cards are traditional removable SIM cards that customers insert into their devices. OmniCRM manages these through the inventory system and provisions them to the HSS/IMS for network access.
Physical SIM Workflow
1. Inventory Setup
SIM cards must first be loaded into the inventory system:
- ICCID (Integrated Circuit Card Identifier) - Unique SIM card identifier
- IMSI (International Mobile Subscriber Identity) - Subscriber identity for the network
These are stored in inventory item fields:
itemtext1: ICCIDitemtext2: IMSIitemtext3: SIM Type (Physical/eSIM) - optional
Example Physical SIM Inventory Item:
{
"inventory_id": 1001,
"item": "SIM Card",
"itemtext1": "8961234567890123456", // ICCID
"itemtext2": "310120123456789", // IMSI
"itemtext3": "Physical", // SIM Type
"item_location": "Warehouse A, Shelf 3",
"item_state": "New",
"wholesale_cost": 2.50,
"retail_cost": 10.00
}
For a complete explanation of all inventory fields (itemtext1-20, item_state values, address fields, management_url, etc.), see Inventory Overview - Inventory Item Fields <administration_inventory>.
Authentication Credentials Storage
The authentication credentials for SIM cards are stored in the HSS AuC (Authentication Center), not in the CRM inventory:
- Ki (Authentication Key) - Secret key for authentication (stored in HSS)
- OPC (Operator Code) - Operator-specific authentication parameter (stored in HSS)
- PIN1/PIN2 - User PIN codes (stored in HSS)
- PUK1/PUK2 - PIN unlock codes (stored in HSS)
The CRM inventory tracks which SIM (by ICCID/IMSI) is assigned to which customer, while the HSS handles the actual network authentication using the Ki/OPC credentials that match the physical SIM card.
2. Service Assignment
When a customer orders a mobile service:
- Staff or customer selects an available SIM card from inventory
- A mobile number (MSISDN) is selected from the phone number inventory
- The product's provisioning playbook is triggered (e.g.,
play_psim_only.yaml)
3. HSS Provisioning
The Ansible playbook provisions the subscriber to the Home Subscriber Server (HSS):
First, it retrieves the auc_id (reference to the authentication credentials already stored in HSS):
- name: Get AuC ID for IMSI
uri:
url: "{{ item }}/auc/imsi/{{ imsi }}"
method: GET
loop: "{{ hss_peers }}"
register: auc_lookup
- name: Extract auc_id
set_fact:
auc_id: "{{ auc_lookup.results[0].json.auc_id }}"
Then creates the subscriber record referencing that auc_id:
- name: Provision subscriber on HSS
uri:
url: "{{ item }}/subscriber/"
method: PUT
body_format: json
body:
enabled: true
roaming_enabled: true
auc_id: "{{ auc_id }}"
msisdn: "{{ phone_number }}"
imsi: "{{ imsi }}"
ue_ambr_dl: 9999999
ue_ambr_ul: 9999999
apn_list: "1,2,3"
default_apn: 1
loop: "{{ hss_peers }}"
The authentication credentials (Ki, OPC) remain in the HSS AuC and are never exposed to the CRM or provisioning playbooks.
4. IMS Provisioning (for Voice Services)
For voice calling capability, an IMS subscriber is created:
- name: Create IMS subscriber for voice services
uri:
url: "{{ item }}/ims_subscriber/"
method: PUT
body_format: json
body:
imsi: "{{ imsi }}"
msisdn: "{{ phone_number }}"
msisdn_list: "{{ phone_number }}"
ifc_path: "default_ifc.xml"
sh_profile: "{{ sh_profile_xml }}"
loop: "{{ hss_peers }}"
5. Billing System Setup
The OCS (Online Charging System) is configured with:
- Account creation
- IMSI/MSISDN mapping via attribute profiles
- Filter rules for identifying the subscriber
- Resource limits (concurrent sessions)
- Initial balance
6. Service Creation and Inventory Assignment
Finally:
- A service record is created in the CRM
- The SIM card inventory item is assigned to the customer (
customer_idset) - The SIM card is linked to the service (
service_idset) - Inventory state is updated to
"Assigned"
Physical SIM Authentication
Physical SIMs use AuC (Authentication Center) credentials stored in the HSS:
- Ki and OPC are used for 4G/5G authentication (MILENAGE algorithm)
- These credentials are pre-loaded into the HSS AuC database during SIM import
- The physical SIM card contains matching Ki/OPC values
- During provisioning, the playbook references the auc_id to link the subscriber to these credentials
- Network authentication happens via challenge-response between the SIM and HSS
- The CRM never sees or handles the actual Ki/OPC values
eSIM Provisioning
eSIMs (embedded SIMs) are software-based SIM profiles that can be downloaded to compatible devices without physical SIM cards. OmniCRM supports eSIM provisioning with LPA (Local Profile Assistant) activation codes.
eSIM Features
LPA Activation Codes
eSIMs use LPA codes for activation:
LPA:1$smdp.example.com$ACTIVATION-CODE-ABC123XYZ
Where:
LPA:1- LPA version identifiersmdp.example.com- SM-DP+ (Subscription Manager Data Preparation) server addressACTIVATION-CODE-ABC123XYZ- Unique activation code for this eSIM profile
QR Code Generation
OmniCRM automatically generates QR codes from LPA activation codes:
- Stored in inventory
management_urlfield - Displayed in the UI as scannable QR code
- Customers scan with their device camera to install the eSIM profile
- No manual typing of long activation codes required
Example eSIM Inventory Item:
{
"inventory_id": 1002,
"item": "eSIM",
"itemtext1": "8961234567890123457",
"itemtext2": "310120123456790",
"itemtext3": "eSIM",
"management_url": "LPA:1$smdp.example.com$ACTIVATION-CODE-ABC123XYZ",
"item_location": "Virtual Inventory",
"item_state": "New",
"wholesale_cost": 0.00,
"retail_cost": 0.00
}
When viewing this inventory item in the UI, a QR code is automatically generated and displayed for easy scanning.
eSIM Provisioning Workflow
The eSIM provisioning workflow is nearly identical to physical SIM provisioning, with a few key differences:
1. Automatic eSIM Assignment
If a product requires a SIM but no physical SIM is selected, the system can automatically assign an available eSIM:
# Automatic eSIM lookup
search_filters = {
"customer_id": [None],
"item_state": ["New"],
"itemtext3": ["eSIM"] # Filter for eSIM type
}
available_esim = search_inventory("SIM Card", filters=search_filters)
2. HSS/IMS Provisioning
eSIMs are provisioned to HSS/IMS exactly like physical SIMs:
- Same IMSI provisioning
- Same IMS subscriber creation
- Same authentication credentials (Ki, OPC)
3. Customer Activation
After provisioning:
- Customer receives eSIM activation details via email or in the customer portal
- QR code is displayed for scanning
- Customer scans QR code with their device
- Device downloads the eSIM profile from the SM-DP+ server
- eSIM is installed and ready to use
Public eSIM Issuer API
OmniCRM provides a public API endpoint for self-service eSIM requests, enabling customers to request an eSIM directly from a website or mobile app without staff intervention.
Endpoint: POST /crm/oam/issue-esim
Request Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
email | String | Yes | - | Customer email address for eSIM delivery. Must be a valid email format. |
name | String | No | Derived from email | Customer name for email personalization. If not provided, the local part of the email address is used. |
customer_id | Integer | No | null | Existing customer ID. When provided, creates an activity record linked to the customer for tracking. |
device_info | Object | No | {} | Device information for analytics and compatibility tracking. |
device_info.compatibility_status | String | No | "unknown" | eSIM compatibility status: supported, may-support, or unknown. Used for analytics. |
device_info.user_agent | String | No | Request header | User agent string. Auto-captured from request headers if not provided. |
Rate Limiting
The endpoint implements dual rate limiting to prevent abuse:
| Limit | Window | Scope | Description |
|---|---|---|---|
| 10 requests | 1 minute | Per IP address | General rate limit applied via decorator |
| 3 requests | 24 hours | Per email address | Prevents excessive eSIM requests to same email |
When rate limits are exceeded, the endpoint returns HTTP 429 with error_type: "RateLimitExceeded".
Response Format
Success Response (HTTP 200):
{
"result": "Success",
"message": "eSIM request received. You will receive your eSIM QR code via email shortly.",
"esim_id": 8472,
"iccid": "8944200000001234567",
"email_sent": true
}
| Field | Type | Description |
|---|---|---|
result | String | Always "Success" for HTTP 200 responses |
message | String | Human-readable confirmation message |
esim_id | Integer | The inventory ID of the assigned eSIM |
iccid | String | The ICCID of the assigned eSIM |
email_sent | Boolean | Whether the delivery email was successfully sent |
Error Responses
| HTTP Code | Error Type | Description |
|---|---|---|
| 400 | - | Invalid or missing email address |
| 429 | RateLimitExceeded | Per-email or per-IP rate limit exceeded |
| 503 | NoInventory | No available eSIMs in inventory |
| 500 | - | Internal server error |
Error Response Format:
{
"result": "Failed",
"reason": "Error description",
"error_type": "ErrorType"
}
Email Delivery
The endpoint sends an eSIM delivery email via Mailjet containing:
- ICCID
- QR code (if available in
itemtext2) - Activation code/LPA string (if available in
itemtext3ormanagement_url) - Step-by-step activation instructions
Configure the Mailjet template in crm_config.yaml:
mailjet:
api_crmCommunicationEsimDelivery:
from_email: "support@aimobile.ac"
from_name: "Ascension Island Mobile"
template_id: 1234567
subject: "Your eSIM is Ready"
| Parameter | Type | Required | Description |
|---|---|---|---|
from_email | String | Yes | Sender email address |
from_name | String | Yes | Sender display name |
template_id | Integer | No | Mailjet template ID. If not provided, a default HTML email is generated. |
subject | String | Yes | Email subject line |
If no template is configured, the system generates a branded HTML email with:
- Company branding and logo
- QR code for eSIM activation (if available)
- Manual activation code instructions
- Device-specific setup guidance for iOS and Android
Activity Logging
Each eSIM request creates an activity record with:
- Activity type:
esim_request - Customer ID linkage (if provided)
- Device compatibility status
- User agent information
This enables tracking of eSIM requests and conversion analytics.
4. eSIM Data Structure in HSS
eSIMs in the HSS AuC (Authentication Center) are marked with an esim: true flag:
{
"esim": true,
"lpa": "LPA:1$smdp.example.com$ACTIVATION-CODE-ABC123XYZ",
"iccid": "8961234567890123457",
"imsi": "310120123456790",
"ki": "00112233445566778899AABBCCDDEEFF",
"opc": "FFEEDDCCBBAA99887766554433221100",
"pin1": "1234",
"pin2": "5678",
"puk1": "12345678",
"puk2": "87654321",
"batch_name": "eSIM_Batch_2024_Q1",
"sim_vendor": "Thales"
}
eSIM Import Process
eSIMs are typically imported in bulk from the eSIM provider:
Import Script: /OmniCRM-API/Provisioners/customer_product_creation/import_keys_esim.py
The import process:
- Reads eSIM data from Excel files provided by the vendor
- Loads LPA activation codes from CSV files
- Updates the HSS with eSIM credentials
- Creates corresponding inventory items in the CRM
- Links eSIM profiles to the inventory system
This ensures that eSIM profiles are available for assignment while maintaining proper inventory tracking.
Inventory Integration
The inventory system is central to SIM provisioning, tracking both physical and virtual resources needed for mobile services.
SIM Card Inventory Template
A typical SIM Card inventory template defines:
{
"item": "SIM Card",
"itemtext1_label": "ICCID",
"itemtext2_label": "IMSI",
"itemtext3_label": "SIM Type",
"wholesale_cost": 2.50,
"retail_cost": 10.00
}
Note: Authentication credentials (Ki, OPC, PIN, PUK) are stored in the HSS AuC, not in the CRM inventory.
For detailed information on creating and managing inventory templates, including how to define custom fields (itemtext1-20) and link templates to products, see Inventory Management - Inventory Templates <administration_inventory> section.
Mobile Number Inventory
Phone numbers are managed as separate inventory items:
{
"inventory_id": 4001,
"item": "Mobile Number",
"itemtext1": "+61412345678",
"itemtext2": "Melbourne",
"itemtext3": "Mobile",
"item_location": "Australia - VIC",
"item_state": "New",
"wholesale_cost": 1.00,
"retail_cost": 0.00
}
Inventory States for SIMs
SIM cards progress through several inventory states:
- New - Unused SIM, available for assignment
- Assigned - Currently active with a customer
- Used - Previously assigned, returned to inventory (can be reused)
- Internal Use - For testing or staff use
- Damaged - Non-functional, requires replacement
- Lost - Cannot be located
- Stolen - Reported stolen
Product Integration
Products specify required inventory via inventory_items_list:
{
"product_id": 1,
"product_slug": "Mobile-SIM",
"product_name": "Mobile SIM Only",
"provisioning_play": "play_psim_only",
"inventory_items_list": "['SIM Card', 'Mobile Number']"
}
When provisioning this product:
- UI displays inventory picker with two dropdowns
- User selects available SIM Card (physical or eSIM)
- User selects available Mobile Number
- Inventory IDs are passed to the Ansible playbook
- Playbook retrieves full inventory details via API
- Provisioning proceeds with selected resources
For a complete explanation of how products drive provisioning, how inventory requirements are defined, and how variables are passed to playbooks, see Provisioning System - How Products Drive Provisioning <concepts_provisioning>.
Inventory Assignment Flow
The Ansible playbook handles inventory assignment.
For more details on how playbooks authenticate with the CRM API (Bearer tokens, API keys, refresh tokens), see Provisioning System - Authentication and Authorization <concepts_provisioning>.
- name: Get SIM Card details from inventory
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ inventory_id_sim_card }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
register: sim_inventory_response
- name: Extract SIM identifiers
set_fact:
iccid: "{{ sim_inventory_response.json.itemtext1 }}"
imsi: "{{ sim_inventory_response.json.itemtext2 }}"
- name: Get AuC ID from HSS (reference to authentication credentials)
uri:
url: "{{ item }}/auc/imsi/{{ imsi }}"
method: GET
loop: "{{ hss_peers }}"
register: auc_response
- name: Extract AuC ID
set_fact:
auc_id: "{{ auc_response.results[0].json.auc_id }}"
# ... provision to HSS/IMS/OCS ...
- name: Assign SIM to customer
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ inventory_id_sim_card }}"
method: PATCH
headers:
Authorization: "Bearer {{ access_token }}"
body_format: json
body:
customer_id: "{{ customer_id }}"
service_id: "{{ service_id }}"
item_state: "Assigned"
This ensures:
- SIM is marked as assigned to the customer
- Service ID is linked for tracking
- Inventory state reflects current status
- Other systems can query inventory to find customer's SIM
Self-Signup with SIM Provisioning
OmniCRM supports customer self-service signup where customers can activate their own SIM cards or eSIMs without staff intervention.
Self-Signup Flow
1. Customer Initiates Signup
Customer visits self-signup page and begins account creation:
- Provides personal information
- Selects a service plan (product)
- Enters payment details
2. SIM Number Validation
Customer can provide their own SIM number (optional):
GET /auth/self-signup/validate-itemtext1/{sim_number}
This endpoint checks if:
- The SIM exists in inventory
- The SIM is available (not already assigned)
- The SIM is in the correct state ("New" or "Used")
3. Automatic SIM Assignment (If No SIM Provided)
If the customer doesn't have a SIM, the system auto-assigns one:
def _populate_base_plan_inventory(sim_number, plan):
if sim_number:
# Customer provided SIM - verify it exists
sim_inventory = get_inventory_by_itemtext1(session, sim_number, None)
if not sim_inventory:
# Check if there's an inactive service with this number
service = get_inactive_service_by_sim(sim_number)
if service:
sim_inventory = get_sim_for_service(service.service_id)
else:
# Auto-assign available SIM from inventory
sim_inventory = get_first_available_inventory_by_item_name(
session,
"SIM Card",
filters={"item_state": ["New"], "customer_id": [None]}
)
# Get ICCID and MSISDN
iccid = sim_inventory.itemtext1
# Auto-assign phone number
number_inventory = get_first_available_inventory_by_item_name(
session,
"Mobile Number"
)
msisdn = number_inventory.itemtext1
return {
"SIM Card": sim_inventory.inventory_id,
"Mobile Number": number_inventory.inventory_id
}
4. Account Creation and Provisioning
Once payment is authorized:
def _post_signup_tasks(customer_id, plan, payment_method):
# 1. Save payment method
save_stripe_payment_method(customer_id, payment_method)
# 2. Charge customer card
charge_customer_card(customer_id, plan.retail_setup_cost)
# 3. Login user (get JWT tokens)
tokens = login_customer(customer_id)
# 4. Populate inventory (SIM and phone number)
inventory_vars = _populate_base_plan_inventory(sim_number, plan)
# 5. Create provisioning job
provision_vars = {
"product_id": plan.product_id,
"customer_id": customer_id,
**inventory_vars # Include SIM Card and Mobile Number IDs
}
# Optional: Chain addon products
if plan.addon_chain:
provision_vars["addon_chain"] = plan.addon_chain
provision_id = create_provisioning_job(provision_vars)
# 6. Return success with tokens
return {
"access_token": tokens.access_token,
"refresh_token": tokens.refresh_token,
"customer_id": customer_id,
"provision_id": provision_id
}
5. Customer Receives Activation Details
After provisioning completes:
- Physical SIM: Welcome email with phone number and activation instructions
- eSIM: Email includes QR code for eSIM profile download, or customer can view QR code in customer portal
6. Special Permissions for Self-Signup
During signup, the JWT token includes a special claim:
{
"sub": "customer_123",
"signup_process": true,
"permissions": ["VIEW_OWN_PROVISION", "UPDATE_OWN_INVENTORY"]
}
This allows:
- Customer to read/update inventory during signup
- Restricted to their own customer account only
- Prevents assigning SIMs belonging to other customers
Self-Signup Security
Validation Checks:
- SIM must exist in inventory
- SIM must not be assigned to another customer
- SIM must be in acceptable state ("New", "Used")
- Customer can only assign SIMs to their own account
- Payment must be authorized before provisioning
Inventory Safeguards:
- Database transactions ensure atomic assignment
- Concurrent signup attempts are prevented via database locks
- Inventory state changes are logged for audit
Ansible-Based Provisioning
All SIM provisioning is orchestrated through Ansible playbooks, providing a consistent, auditable, and repeatable process.
For comprehensive documentation on Ansible playbook structure, the block/rescue pattern, how playbooks interact with products, and best practices, see Ansible Playbooks: Detailed Guide <concepts_ansible>.
Main SIM Provisioning Playbook
File: /OmniCRM-API/Provisioners/plays/play_psim_only.yaml
This is the primary playbook for mobile SIM provisioning (both physical and eSIM). It handles:
- Mobile voice & data services
- Data-only services
- Voice-only IMS subscribers
Playbook Structure (1,561 lines):
For detailed explanation of playbook headers, the hosts/gather_facts/become directives, and overall structure, see Ansible Playbooks - Playbook Structure and Anatomy <concepts_ansible>.
- name: OmniCore Service Provisioning 2024
hosts: localhost
gather_facts: no
become: False
tasks:
- name: Main provisioning block
block:
# --- PRE-PROVISIONING (Lines 240-396) ---
- name: Get SIM information from inventory
# Retrieves IMSI, ICCID, etc.
- name: Validate IMSI length
# Must be exactly 15 digits
- name: Check if IMSI exists in HSS
# Prevents duplicate provisioning
- name: Get AuC ID for IMSI
# Retrieves auc_id reference from HSS
# --- HSS PROVISIONING (Lines 398-534) ---
- name: Create/Update HSS Subscriber
# Provisions to all HSS peers
- name: Create IMS Subscriber
# Enables voice calling
# --- OCS SETUP (Lines 536-837) ---
- name: Create ENUM entry
# E.164 number mapping
- name: Create FilterS rule
# Account identification
- name: Create AttributeS profile
# IMSI/MSISDN mapping
- name: Create ResourceS profile
# Concurrent session limits
- name: Create OCS account
# Billing account creation
# --- SERVICE & INVENTORY (Lines 865-946) ---
- name: Create service record
# In CRM database
- name: Assign SIM to service
# Update inventory
- name: Send welcome SMS
# Customer notification
rescue:
# --- CLEANUP/DEPROVISIONING (Lines 975-1562) ---
- name: Return inventory to pool
# Reset customer_id, service_id to null
- name: Delete OCS account
# Remove billing account
- name: Remove HSS subscribers
# Or set to dormant state
- name: Determine success/failure
assert:
that:
- action == "deprovision"
Key Provisioning Steps
1. Pre-Provisioning Validation
- name: Get SIM information from CRM inventory
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ inventory_id_sim_card }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
register: api_response_sim
- name: Extract and validate IMSI
set_fact:
imsi: "{{ api_response_sim.json.itemtext2 }}"
- name: Validate IMSI format
assert:
that:
- imsi | length == 15
- imsi is match('^[0-9]+$')
fail_msg: "IMSI must be exactly 15 digits"
2. Retrieve AuC ID from HSS
- name: Get AuC ID for IMSI from HSS
uri:
url: "{{ item }}/auc/imsi/{{ imsi }}"
method: GET
loop: "{{ crm_config.hss.hss_peers }}"
register: auc_lookup
- name: Extract auc_id from first HSS response
set_fact:
auc_id: "{{ auc_lookup.results[0].json.auc_id }}"
3. HSS Subscriber Provisioning
- name: Create subscriber in each HSS peer
uri:
url: "{{ item }}/subscriber/"
method: PUT
body_format: json
body:
enabled: true
roaming_enabled: true
auc_id: "{{ auc_id }}"
msisdn: "{{ phone_number }}"
imsi: "{{ imsi }}"
ue_ambr_dl: 9999999 # Download speed limit
ue_ambr_ul: 9999999 # Upload speed limit
apn_list: "1,2,3"
default_apn: 1
status_code: [200, 201]
loop: "{{ crm_config.hss.hss_peers }}"
register: hss_responses
4. IMS Subscriber Provisioning (Voice)
- name: Create IMS subscriber for voice services
uri:
url: "{{ item }}/ims_subscriber/"
method: PUT
body_format: json
body:
imsi: "{{ imsi }}"
msisdn: "{{ phone_number }}"
msisdn_list: "{{ phone_number }}"
ifc_path: "default_ifc.xml"
sh_profile: |
<?xml version="1.0"?>
<IMSSubscription>
<PrivateID>{{ imsi }}@ims.mnc{{ mnc }}.mcc{{ mcc }}.3gppnetwork.org</PrivateID>
<ServiceProfile>
<PublicIdentity>
<Identity>sip:{{ phone_number }}@ims.mnc{{ mnc }}.mcc{{ mcc }}.3gppnetwork.org</Identity>
</PublicIdentity>
</ServiceProfile>
</IMSSubscription>
status_code: [200, 201]
loop: "{{ crm_config.hss.hss_peers }}"
5. OCS Billing Configuration
# Create account filters (identify subscriber by IMSI/MSISDN)
- name: Create FilterS for IMSI identification
uri:
url: "{{ crm_config.ocs.ocsApi }}/jsonrpc"
method: POST
body_format: json
body:
method: "ApierV1.SetFilter"
params:
- ID: "FILTER_IMSI_{{ service_uuid }}"
Type: "*string"
Element: "~*req.OriginHost"
Values: ["{{ imsi }}"]
# Create attribute profile (map IMSI to account)
- name: Create AttributeS profile
uri:
url: "{{ crm_config.ocs.ocsApi }}/jsonrpc"
method: POST
body_format: json
body:
method: "ApierV1.SetAttributeProfile"
params:
- ID: "ATTR_ACCOUNT_{{ service_uuid }}"
FilterIDs: ["FILTER_IMSI_{{ service_uuid }}"]
Attributes:
- Path: "*req.Account"
Value: "{{ service_uuid }}"
# Create billing account
- name: Create OCS account
uri:
url: "{{ crm_config.ocs.ocsApi }}/jsonrpc"
method: POST
body_format: json
body:
method: "ApierV2.SetAccount"
params:
- Tenant: "{{ crm_config.ocs.ocsTenant }}"
Account: "{{ service_uuid }}"
ActionPlanIds: []
ExtraOptions:
AllowNegative: false
Disabled: false
# Add initial balance
- name: Add initial monetary balance
uri:
url: "{{ crm_config.ocs.ocsApi }}/jsonrpc"
method: POST
body_format: json
body:
method: "ApierV1.AddBalance"
params:
- Tenant: "{{ crm_config.ocs.ocsTenant }}"
Account: "{{ service_uuid }}"
BalanceType: "*monetary"
Balance:
Value: 0
ExpiryTime: "+8760h" # 1 year
6. Service Creation
- name: Create service record in CRM
uri:
url: "{{ crm_config.crm.base_url }}/crm/service/"
method: PUT
headers:
Authorization: "Bearer {{ access_token }}"
body_format: json
body:
customer_id: "{{ customer_id }}"
product_id: "{{ product_id }}"
service_name: "Mobile Service - {{ phone_number }}"
service_uuid: "{{ service_uuid }}"
service_status: "Active"
retail_cost: "{{ monthly_cost }}"
status_code: 200
register: service_creation_response
- name: Extract service_id
set_fact:
service_id: "{{ service_creation_response.json.service_id }}"
7. Inventory Assignment
- name: Assign SIM card to customer and service
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ inventory_id_sim_card }}"
method: PATCH
headers:
Authorization: "Bearer {{ access_token }}"
body_format: json
body:
customer_id: "{{ customer_id }}"
service_id: "{{ service_id }}"
item_state: "Assigned"
sold_date: "{{ ansible_date_time.iso8601 }}"
- name: Assign phone number to customer and service
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ inventory_id_phone_number }}"
method: PATCH
headers:
Authorization: "Bearer {{ access_token }}"
body_format: json
body:
customer_id: "{{ customer_id }}"
service_id: "{{ service_id }}"
item_state: "Assigned"
sold_date: "{{ ansible_date_time.iso8601 }}"
Deprovisioning and Rollback
The same playbook handles both failed provision rollback and intentional deprovisioning using Ansible's rescue block.
For a detailed explanation of the block/rescue pattern and why this is best practice, see Provisioning System - Rollback and Cleanup: Best Practice Pattern <concepts_provisioning>.
rescue:
# This section runs when:
# 1. Any task in block fails (automatic rollback)
# 2. action == "deprovision" (intentional cleanup)
- name: Get inventory items linked to service
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/customer_id/{{ customer_id }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
register: inventory_items
ignore_errors: true
- name: Return inventory to available pool
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ item.inventory_id }}"
method: PATCH
headers:
Authorization: "Bearer {{ access_token }}"
body_format: json
body:
customer_id: null
service_id: null
item_state: "Used"
loop: "{{ inventory_items.json.data }}"
when: inventory_items.json.data is defined
ignore_errors: true
- name: Delete OCS account
uri:
url: "{{ crm_config.ocs.ocsApi }}/jsonrpc"
method: POST
body_format: json
body:
method: "ApierV1.RemoveAccount"
params:
- Tenant: "{{ crm_config.ocs.ocsTenant }}"
Account: "{{ service_uuid }}"
ignore_errors: true
- name: Delete or disable HSS subscriber
uri:
url: "{{ item.key }}/subscriber/{{ item.value.subscriber_id }}"
method: DELETE
loop: "{{ hss_subscriber_data | dict2items }}"
when: deprovision_subscriber | default(false) | bool
ignore_errors: true
# Final assertion determines success or failure
- name: Determine if this was intentional deprovision or failed provision
assert:
that:
- action == "deprovision"
fail_msg: "Provisioning failed and rollback completed"
success_msg: "Deprovisioning completed successfully"
Key Points:
ignore_errors: trueon all cleanup tasks ensures all cleanup attempts are made- If
action == "deprovision", the assertion passes (status 0 = success) - If
actionis not set, the assertion fails (status 2 = failed provision) - This ensures atomic operations: either fully provisioned or fully cleaned up
Other SIM-Related Playbooks
Local Development Playbook:
play_local_mobile_sim.yaml - Development version that skips external HSS/OCS dependencies
MSISDN Swap:
play_swap_msisdn.yaml - Swaps phone numbers between existing services
Topup/Recharge Playbooks:
play_topup_monetary.yaml- Add monetary balanceplay_topup_no_charge.yaml- Add free data/minutesplay_topup_dongle.yaml- Topup for IoT/data devices
HSS and IMS Integration
OmniCRM integrates with PyHSS (open-source Home Subscriber Server) for subscriber management and authentication.
HSS Functions
File: /OmniCRM-API/Provisioners/hss.py
Key Functions:
def ProvisionMobileSIM(json_data, hss_urls):
"""
Provisions a mobile SIM to HSS and IMS
Args:
json_data: Dict with IMSI, MSISDN, authentication credentials
hss_urls: List of HSS peer URLs
Returns:
Dict with subscriber IDs from each HSS peer
"""
# Create HSS subscriber (for data)
# Create IMS subscriber (for voice)
# Return subscriber IDs for tracking
def get_subscriber_info_msisdn(msisdn, hss_urls):
"""
Retrieves subscriber details by phone number
Args:
msisdn: Phone number to lookup
hss_urls: List of HSS peer URLs
Returns:
Subscriber information from HSS
"""
Multi-HSS Support
OmniCRM supports provisioning to multiple HSS peers for redundancy:
# crm_config.yaml
hss:
hss_peers:
- "http://10.12.64.140:8080"
- "http://10.12.64.141:8080"
apn_list: "1,2,3"
default_apn: 1
Provisioning happens across all configured HSS instances to ensure:
- Geographic redundancy
- Load balancing
- Failover capability
- Synchronized subscriber data
Authentication Center (AuC)
The AuC stores subscriber authentication credentials. During provisioning, only the auc_id is retrieved:
Get AuC ID for IMSI:
GET /auc/imsi/{imsi}
Response:
{
"auc_id": 12345,
"imsi": "310120123456789",
"iccid": "8961234567890123456",
"esim": false,
"lpa": null
}
For eSIMs, the esim flag is true and lpa contains the activation code for display to customers.
Note: The actual authentication credentials (Ki, OPC, PIN, PUK) remain in the HSS and are never exposed via API. The provisioning system only needs the auc_id to reference these credentials when creating subscribers.
OCS Integration
The Online Charging System (OCS) handles real-time charging and rating for mobile services. OmniCRM uses CGRateS as the OCS platform.
OCS Configuration
File: /OmniCRM-API/Provisioners/ocs.py
Configuration:
# crm_config.yaml
ocs:
cgrates: "10.64.12.160:2080"
ocsTenant: "mnc380.mcc313.3gppnetwork.org"
ocsApi: "http://10.64.12.160:2080"
OCS Components for SIM Services
1. Filters - Subscriber Identification
Filters identify the subscriber from network requests:
{
"ID": "FILTER_IMSI_Service_abc123",
"Type": "*string",
"Element": "~*req.OriginHost",
"Values": ["310120123456789"]
}
2. Attributes - Account Mapping
Attributes map the IMSI to the billing account:
{
"ID": "ATTR_ACCOUNT_Service_abc123",
"FilterIDs": ["FILTER_IMSI_Service_abc123"],
"Attributes": [
{
"Path": "*req.Account",
"Value": "Service_abc123"
},
{
"Path": "*req.Bandwidth",
"Value": "100000000" // 100 Mbps
}
]
}
3. Resources - Session Limits
Resources control concurrent session limits:
{
"ID": "RES_SESSIONS_Service_abc123",
"FilterIDs": ["FILTER_IMSI_Service_abc123"],
"Limit": 5, // Max 5 concurrent sessions
"UsageTTL": "-1"
}
4. Stats - Usage Tracking
Stats queues track usage for analytics:
{
"ID": "STATS_Service_abc123",
"FilterIDs": ["FILTER_IMSI_Service_abc123"],
"Metrics": [
"*sum#~*req.Usage",
"*tcc" // Total call count
]
}
5. Action Plans - Recurring Charges
Action plans handle recurring operations like monthly charges:
{
"Id": "ActionPlan_Service_abc123_Monthly",
"ActionsId": "Action_Add_Monthly_Data",
"Timing": {
"MonthDays": [1], // 1st of each month
"Time": "00:00:00Z"
}
}
OCS API Functions
Key Functions from ocs.py:
async def async_get_balance(account, server, tenant):
"""Get current account balance"""
async def async_topup_dongle(tenant, account, server, days):
"""Add time-based data allowance"""
def Get_Account_Status(account, server, tenant):
"""Get comprehensive account information"""
def CGRateS_API_Call(method, params, server, tenant):
"""Generic CGRateS JSON-RPC API call"""
SIM Inventory Reconciliation
OmniCRM includes tools to ensure consistency between the CRM inventory and the HSS.
Reconciliation Script
File: /OmniCRM-API/Provisioners/hss_reconcile.py
Purpose:
- Identifies SIMs in HSS but not in inventory (orphaned)
- Finds assigned SIMs not provisioned in HSS
- Validates MSISDN formats
- Generates HTML reports
Checks Performed:
-
Sold SIMs in HSS:
- Query all assigned SIMs from inventory
- Check if each IMSI exists in HSS
- Report SIMs assigned to customers but not in HSS
-
Provisioned SIMs in Inventory:
- Query all subscribers from HSS
- Check if each IMSI exists in inventory
- Report orphaned HSS entries
-
MSISDN Validation:
- Validate phone number formats
- Check for Australian number compliance
- Report malformed numbers
Running Reconciliation:
python hss_reconcile.py
Output:
- HTML report with color-coded results
- Lists of mismatches for investigation
- Recommendations for cleanup
Best Practices:
- Run reconciliation weekly
- Investigate discrepancies promptly
- Use reports to identify provisioning failures
- Clean up orphaned entries to free resources
Troubleshooting SIM Provisioning
Common Issues and Solutions
Issue: SIM provisioning fails with "IMSI already exists in HSS"
- Cause: IMSI is already provisioned from a previous attempt
- Solution: Check HSS for existing subscriber, either:
- Delete the existing subscriber if it's orphaned
- Update the existing subscriber instead of creating new
- Run reconciliation to identify the conflict
Issue: eSIM QR code not generating
- Cause:
management_urlfield not populated with LPA code - Solution: Ensure eSIM import script sets
management_urlcorrectly - Format: Must be
LPA:1$server$activation-code
Issue: Inventory item shows as "Available" but is assigned
- Cause: Provisioning failed before inventory assignment
- Solution: Check provisioning events for failure reason
- Fix: Either complete provisioning or return inventory to pool
Issue: Customer cannot make calls (data works)
- Cause: IMS subscriber not provisioned
- Solution: Check if IMS provisioning step completed
- Fix: Manually provision IMS subscriber or re-run playbook
Issue: Authentication failures (SIM cannot connect)
- Cause: Ki/OPC mismatch between SIM card and HSS
- Solution: Verify AuC credentials match physical SIM
- Fix: Update HSS AuC with correct credentials
Issue: Self-signup fails to assign SIM
- Cause: No available SIMs in inventory
- Solution: Check inventory for SIMs with state "New" and customer_id NULL
- Fix: Import new SIM batch or mark returned SIMs as "Used"
Debugging Tips
1. Check Provisioning Events:
GET /crm/provision/provision_id/{id}
Look for failed tasks (status 2) and review error messages.
2. Verify HSS Subscriber:
GET {hss_url}/subscriber/msisdn/{phone_number}
Check if subscriber exists and has correct configuration.
3. Check OCS Account:
{
"method": "ApierV2.GetAccount",
"params": [
{
"Tenant": "mnc380.mcc313.3gppnetwork.org",
"Account": "Service_abc123"
}
]
}
4. Query Inventory Status:
GET /crm/inventory/?filters={"itemtext2":["310120123456789"]}
Search by IMSI to find inventory item and check its state.
5. Review Ansible Logs:
Check /var/log/ansible/ or provisioning event details for full playbook output.
Best Practices
SIM Inventory Management
- Bulk Import: Use import scripts for large SIM batches
- Regular Reconciliation: Run weekly HSS/inventory reconciliation
- State Management: Keep inventory states accurate (New, Assigned, Used)
- Vendor Tracking: Record SIM vendor and batch information
- Cost Tracking: Maintain accurate wholesale/retail costs
Security
- Credential Separation: Authentication credentials (Ki/OPC) remain in HSS only, never in CRM
- Access Control: Limit who can assign/modify SIM inventory
- Audit Logging: Track all SIM assignments and changes
- PIN/PUK Management: PIN/PUK codes stored in HSS, provided to customers via secure channels
- Lost/Stolen Process: Immediately suspend service in HSS and mark inventory state
Provisioning
- Validation: Always validate IMSI format and uniqueness
- Rollback: Use block/rescue for automatic cleanup on failure
- Notifications: Send clear activation instructions to customers
- Testing: Test provisioning in development before production
- Monitoring: Track provisioning success rates and failure reasons
eSIM Specific
- LPA Format: Validate LPA code format before storing
- QR Testing: Test QR codes on multiple devices
- Activation Instructions: Provide clear step-by-step guides
- Profile Management: Track eSIM profile downloads and installations
- Backup Codes: Provide manual LPA codes in case QR scanning fails
Related Documentation
Provisioning System <concepts_provisioning>- Complete provisioning workflow detailsAnsible Playbooks <concepts_ansible>- Playbook structure and best practicesInventory Management <administration_inventory>- Inventory system overviewProducts and Services <concepts_products_and_services>- Product configurationCustomer Care <customer_care>- Supporting customers with SIM issuesTop-Up and Recharge <features_topup_recharge>- Adding balance to mobile services