Spenza provides various APIs to manage the mobile subscriptions offered in the Spenza marketplace. This document provides the information required to interact with Spenza APIs.
All API endpoints (except the authentication endpoint) require a Bearer token in the Authorization header. You must first obtain this token using the Authentication API.
| Header | Value | Description |
|---|---|---|
| Authorization | Bearer YOUR_TOKEN |
The access token obtained from the Authentication API |
| Content-Type | application/json |
All requests must send and receive JSON data |
Authorization: Bearer eyJhbGciOiJ...
Content-Type: application/json
Note: Replace YOUR_TOKEN with the actual access token received from the Authentication API.
All API calls require authentication using a Bearer Token. You must first obtain this token using the Authenticate API.
GET/api/v1/authenticate
{
"key": "api-key",
"secret": "api-secret"
}
| Field | Type | Description |
|---|---|---|
| key | string | Your API key |
| secret | string | Your API secret |
{
"message": "Authentication Success.",
"accessToken": "Your Auth Token for other APIs.",
"expiration": "token expiration time"
}
/api/v1/sim-by-iccid
{
"ICCID": "9999967890123456789"
}
| Field | Type | Description |
|---|---|---|
| ICCID | string | The ICCID of the SIM card |
{
"message": "Success.",
"simInfo": {
"status": {
"user": "Assigned",
"device": "Assigned"
},
"operator": {
"custNbr": null
},
"ICCID": "9999967890123456784",
"activationCode": "",
"source": "Other",
"isDeleted": false,
"recommendedPlan": "60af49af21647e2ca0b15f51",
"_id": "632808a8e85110002e3ae629",
"network": "T-Mobile",
"Operator": "T-Mobile",
"phoneNumber": "78993093857",
"group": "63280750e85110002e3ae624",
"account": "63280446e85110002e3ae54e",
"phoneLineStatus": "ACTIVATED"
}
}
/api/v1/plan-by-id
planId: "AT0001"
| Field | Type | Description |
|---|---|---|
| planId | string | Unique identifier for the plan |
/api/v1/purchase-plan
{
"ICCID": "8901260853182965429",
"productId": "product1",
"activateNow": true,
"scheduledDate": "2024-04-20"
}
| Field | Type | Description |
|---|---|---|
| ICCID | string | The ICCID of the SIM card |
| productId | string | The ID of the plan to purchase |
| activateNow | boolean | Whether to activate the plan immediately |
| scheduledDate | string | Date to schedule activation (YYYY-MM-DD format) |
{
"message": "Success.",
"subscription": {
"stripe": {
"subId": "sub_1LkKThAq7JKDyJICNJ3l1WWv",
"scheduleId": null,
"price": null
},
"operatorRes": {
"MDN": null,
"description": null
},
"planName": "EXPLAN_100",
"planCode": "EXPLAN_001",
"status": "ACTIVE",
"initiatedDate": "2022-08-21T04:24:06.473Z",
"startDate": "2022-08-21T04:24:05.000Z",
"endDate": "2022-11-21T04:24:05.000Z",
"scheduledDate": "2022-09-20T00:00:00.000Z",
"cancelledDate": null,
"autoTopUp": true,
"subId": "1663734246474",
"Operator": "T-Mobile",
"network": "T-Mobile",
"orderId": "1739-597673-2114"
}
}
/api/v1/activate-subscription
{
"ICCID": "8901260853182965429",
"productId": "product1"
}
| Field | Type | Description |
|---|---|---|
| ICCID | string | The ICCID of the SIM card |
| productId | string | The ID of the plan to activate |
{
"message": "Success",
"subscription": {
"stripe": {
"subId": "sub_1LkKThAq7JKDyJICNJ3l1WWv",
"scheduleId": null,
"price": null
},
"operatorRes": {
"MDN": null,
"description": null
},
"planName": "EXPLAN_101",
"planCode": "EXPLAN_003",
"status": "ACTIVE",
"initiatedDate": "2022-08-21T04:24:06.473Z",
"startDate": "2022-08-21T04:24:05.000Z",
"endDate": "2022-11-21T04:24:05.000Z",
"scheduledDate": "2022-09-20T00:00:00.000Z",
"autoTopUp": true,
"subId": "1663734246474",
"Operator": "T-Mobile",
"network": "T-Mobile",
"orderId": "1739-597673-2114"
}
}
/api/v1/cancel-subscription
{
"ICCID": "9999967890123456789"
}
| Field | Type | Description |
|---|---|---|
| ICCID | string | The ICCID of the SIM card |
{
"message": "Subscription Cancelled.",
"subscription": {
"stripe": {
"subId": "sub_1LkKThAq7JKDyJICNJ3l1WWv",
"scheduleId": null,
"price": null
},
"operatorRes": {
"MDN": null,
"description": null
},
"planName": "EXPLAN_100",
"planCode": "EXPLAN_001",
"status": "CANCELLED",
"initiatedDate": "2022-08-21T04:24:06.473Z",
"startDate": "2022-08-21T04:24:05.000Z",
"endDate": "2022-11-21T04:24:05.000Z",
"scheduledDate": "2022-09-20T00:00:00.000Z",
"autoTopUp": true,
"subId": "1663734246474",
"Operator": "T-Mobile",
"network": "T-Mobile",
"orderId": "1739-597673-2114"
}
}
/api/v1/telco/getUsage
{
"subId": "1745863440782"
}
| Field | Type | Description |
|---|---|---|
| subId | string | The ID of the subscription (found in subId field of subscription object) |
{
"result": {
"usageData": 0,
"usageCalls": 0,
"usageSms": 0,
"usageUpdatedAt": 1747223324287,
"telcoStatus": "ACTIVATED",
"usageDataConverted": {
"format": "B",
"value": 0
},
"network": "Verizon",
"networkCode": "Verizon"
}
}
/api/v1/subscription-by-iccid
{
"ICCID": "9999967890123456789"
}
| Field | Type | Description |
|---|---|---|
| ICCID | string | The ICCID of the SIM card |
{
"message": "Success.",
"subscription": {
"stripe": {
"subId": "sub_1LkKThAq7JKAyJICNJ3l1WWv",
"scheduleId": null,
"price": null
},
"operatorRes": {
"MDN": null,
"description": null
},
"planName": "T-Mobile100",
"planCode": "T-Mobile0001",
"status": "Pending",
"initiatedDate": "2022-09-21T04:24:06.473Z",
"startDate": "2022-09-21T04:24:05.000Z",
"endDate": "2022-10-21T04:24:05.000Z",
"scheduledDate": "2022-09-20T00:00:00.000Z",
"cancelledDate": null,
"autoTopUp": true,
"subId": "1663734246473",
"Operator": "T-Mobile",
"network": "T-Mobile",
"sim": "632a916b961e53002e1298d7",
"account": "63280446e85110002e3ae54e",
"orderId": "1339-597673-2114"
}
}
/api/v1/renew-phone-number
Requests a new phone number for the specified SIM card. This endpoint is rate-limited based on your plan configuration.
{
"ICCID": "9999967890123456789"
}
| Field | Type | Description |
|---|---|---|
| ICCID | string | The ICCID of the SIM card |
Monthly Rate Limits: This endpoint is subject to monthly rate limits based on your plan configuration. The limit is defined in the plan's apiLimits.renewPhoneNumber property. If no limit is set, all requests are allowed.
IP Tracking: All requests are logged with the originating IP address from the x-forwarded-for header or connection IP. IP addresses are monitored for security and auditing purposes.
{
"message": "Request placed successfully, new number will be assigned soon"
}
/api/v1/public/validate-iccid
Validates the format and checksum of an ICCID. This is a public endpoint and does not require authentication.
{
"ICCID": "8901260853182965429"
}
| Field | Type | Description |
|---|---|---|
| ICCID | string | SIM card ICCID to validate |
{
"isValid": true,
"message": "Valid ICCID."
}
/api/v1/public/validate-imei
Validates the format and Luhn checksum of an IMEI. This is a public endpoint and does not require authentication.
{
"IMEI": "356938035643809"
}
| Field | Type | Description |
|---|---|---|
| IMEI | string | Device IMEI to validate |
{
"isValid": true,
"message": "Valid IMEI."
}
/api/v1/purchase-esim
Initiates asynchronous eSIM purchase and phone number provisioning. The API accepts optional user information. If no user data is provided, the system will use the customer's default employee information.
Requires JWT token in Authorization header: Bearer <token>
{
"imei": "451014850281267",
"simId": "TEST_SPENZA"
}
{
"imei": "451014850281267",
"simId": "SpenzaJ_ESIM",
"user": {
"name": "Testung",
"email": "test+8@spenza.com",
"phoneNumber": "2031134322",
"address": "47 W 13th St",
"city": "New York",
"state": "New York",
"zipcode": "10011"
}
}
| Field | Type | Required | Description |
|---|---|---|---|
| imei | string | Yes | Device IMEI number (exactly 15 digits) |
| simId | string | Yes | SIM Product ID / Product name (e.g., "SpenzaJ_ESIM", "TEST_SPENZA") |
| user | object | No | Optional user/employee information. If not provided, system will use customer's default employee. |
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes* | User's full name |
| string | Yes* | User's email address | |
| zipcode | string | Yes* | 5-digit US ZIP code for location-based number provisioning |
| phoneNumber | string | No | User's phone number |
| address | string | No | User's street address |
| city | string | No | User's city |
| state | string | No | User's state |
* Required only if user object is provided
Important: The system uses the ZIP code to provision phone numbers appropriate for the specified location:
user object is provided with an email that matches an existing employee, that employee's ZIP code is used.user object is provided with a new email, a new employee is created with the provided ZIP code.user object is provided, the system uses the customer's default employee ZIP code.{
"requestId": "64dfa874-f157-431a-a6db-d8e5a915ea12",
"status": "PENDING",
"message": "eSIM purchase initiated successfully"
}
// 400 Bad Request - Missing Required Fields
{
"statusCode": 400,
"message": "imei and simId are required fields",
"error": "Bad Request"
}
// 400 Bad Request - Invalid IMEI
{
"statusCode": 400,
"message": "IMEI must be exactly 15 digits",
"error": "Bad Request"
}
// 400 Bad Request - Invalid IMEI Checksum
{
"statusCode": 400,
"message": "Invalid IMEI number - failed Luhn checksum",
"error": "Bad Request"
}
// 400 Bad Request - Missing User Name
{
"statusCode": 400,
"message": "name should not be empty",
"error": "Bad Request"
}
// 400 Bad Request - Missing User Email
{
"statusCode": 400,
"message": "email should not be empty",
"error": "Bad Request"
}
// 400 Bad Request - Invalid Email Format
{
"statusCode": 400,
"message": "email must be a valid email address",
"error": "Bad Request"
}
// 400 Bad Request - Missing User Zipcode
{
"statusCode": 400,
"message": "zipcode should not be empty",
"error": "Bad Request"
}
// 400 Bad Request - Invalid ZIP Code Format (in user object)
{
"statusCode": 400,
"message": "zipcode must be a valid 5-digit US ZIP code",
"error": "Bad Request"
}
// 400 Bad Request - No ZIP Code Available
{
"statusCode": 400,
"message": "No zipCode available. Please provide user information with zipCode or ensure customer/employee has a zipCode on file.",
"error": "Bad Request"
}
// 400 Bad Request - User Data Mismatch with Existing Employee
{
"statusCode": 400,
"message": "User with email test@example.com already exists. Data mismatch detected: zipcode (provided: 10011, existing: 94102), state (provided: New York, existing: California). Please use the existing employee's information.",
"error": "Bad Request"
}
// 400 Bad Request - Product Not Found
{
"statusCode": 400,
"message": "Product not found",
"error": "Bad Request"
}
// 400 Bad Request - Product Not Available
{
"statusCode": 400,
"message": "Product not available for this customer",
"error": "Bad Request"
}
// 401 Unauthorized - Missing or invalid token
{
"statusCode": 401,
"message": "Authentication required",
"error": "Unauthorized"
}
// 500 Internal Server Error
{
"statusCode": 500,
"message": "eSIM purchase failed",
"error": "Internal Server Error"
}
/api/v1/purchase-esim/:requestId
Retrieves the current status of an eSIM purchase. Poll this endpoint to check when the asynchronous purchase completes.
Requires JWT token in Authorization header: Bearer <token>
| Parameter | Type | Description |
|---|---|---|
| requestId | string | UUID returned from the purchase initiation endpoint |
// Status: PENDING (still processing)
{
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"status": "PENDING",
"createdAt": "2024-12-05T10:00:00.000Z",
"updatedAt": "2024-12-05T10:00:00.000Z"
}
// Status: PROCESSING (in progress)
{
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"status": "PROCESSING",
"createdAt": "2024-12-05T10:00:00.000Z",
"updatedAt": "2024-12-05T10:00:15.000Z"
}
// Status: SUCCESS (completed)
{
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"status": "SUCCESS",
"result": {
"mdn": "1234567890",
"iccid": "89012345678901234567",
"qrCode": "https://s3.amazonaws.com/esim-activation-code/customer-id/iccid-esim-activation-qrcode.png"
},
"createdAt": "2024-12-05T10:00:00.000Z",
"updatedAt": "2024-12-05T10:00:45.000Z"
}
// Status: FAILED (error occurred)
{
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"status": "FAILED",
"failureReason": "Insufficient inventory",
"createdAt": "2024-12-05T10:00:00.000Z",
"updatedAt": "2024-12-05T10:00:30.000Z"
}
// 404 Not Found - Request doesn't exist or unauthorized
{
"statusCode": 404,
"message": "Purchase request not found",
"error": "Not Found"
}
// 401 Unauthorized
{
"statusCode": 401,
"message": "Authentication required",
"error": "Unauthorized"
}
/api/v1/webhooks/register
Register or update a webhook for receiving operator events (SMS and/or Voice) for a specific
operator and network combination. If a registration already exists for the
same operator and network, it will be updated with the new configuration.
The system validates that the operator and network combination exists before registering. For SMS event types, the operator must support SMS webhooks.
{
"operator": "SpenzaJ",
"network": "SpenzaJ",
"eventType": "both",
"authentication": {
"type": "basic",
"username": "webhook-user",
"password": "super-secret"
},
"webhookUrls": {
"messageUrl": "https://your-api.com/webhooks/sms/incoming",
"callbackMessageUrl": "https://your-api.com/webhooks/sms/status",
"voiceUrl": "https://your-api.com/webhooks/voice/incoming",
"callbackVoiceUrl": "https://your-api.com/webhooks/voice/status"
},
"retryPolicy": {
"maxAttempts": 5,
"backoffStrategy": "exponential",
"initialDelaySeconds": 5,
"maxDelaySeconds": 300
},
"status": "active",
"description": "Production SMS and Voice webhooks"
}
| Field | Type | Description |
|---|---|---|
| operator | string (required) | Operator name (e.g. SpenzaJ, SpenzaB). Must match a valid operator in the system. |
| network | string (required) | Network name (e.g. SpenzaJ, T-Mobile). Combined with operator, uniquely identifies your registration. |
| eventType | string (required) | sms, voice, or both. Determines which webhook URL fields are required. |
| authentication.type | string (required) | Authentication type for webhook delivery: basic, bearer, api_key, or none. |
| authentication.username | string | Username for Basic authentication. Required when type = basic. |
| authentication.password | string | Password for Basic authentication. Required when type = basic. Stored encrypted. |
| authentication.token | string | Bearer token. Required when type = bearer. Stored encrypted. |
| authentication.apiKey | string | API key value. Required when type = api_key. Stored encrypted. |
| authentication.apiKeyHeader | string | Custom header name for API key (default: X-API-Key). |
| webhookUrls.messageUrl* | string (URL) | URL to receive incoming SMS messages. Required for sms or both. |
| webhookUrls.callbackMessageUrl* | string (URL) | URL to receive SMS delivery status updates (sent, delivered, failed). Required for sms or both. |
| webhookUrls.voiceUrl* | string (URL) | URL to receive incoming voice calls. Required for voice or both. |
| webhookUrls.callbackVoiceUrl* | string (URL) | URL to receive voice call status updates. Required for voice or both. |
| retryPolicy.maxAttempts | number | Maximum retry attempts on delivery failure (1-10, default: 3). |
| retryPolicy.backoffStrategy | string | Retry delay strategy: fixed, exponential, or linear (default: exponential). |
| retryPolicy.initialDelaySeconds | number | Initial delay before first retry in seconds (1-60, default: 5). |
| retryPolicy.maxDelaySeconds | number | Maximum delay between retries in seconds (60-3600, default: 300). |
| status | string | Registration status: active or inactive (default: active). |
| description | string | Optional description or notes about this registration. |
Fields marked with * are conditionally required based on eventType (sms, voice, or both). Only one registration is allowed per operator + network combination. Calling this endpoint again with the same operator and network will update the existing registration.
{
"success": true,
"message": "Webhook registration created successfully",
"data": {
"registrationId": "69146d70ed68e3890a45c228",
"operator": "SpenzaJ",
"network": "SpenzaJ",
"eventType": "both",
"status": "active",
"authentication": {
"type": "basic",
"username": "webhook-user",
"password": "***ENCRYPTED***"
},
"webhookUrls": {
"messageUrl": "https://your-api.com/webhooks/sms/incoming",
"callbackMessageUrl": "https://your-api.com/webhooks/sms/status",
"voiceUrl": "https://your-api.com/webhooks/voice/incoming",
"callbackVoiceUrl": "https://your-api.com/webhooks/voice/status"
},
"retryPolicy": {
"maxAttempts": 5,
"backoffStrategy": "exponential",
"initialDelaySeconds": 5,
"maxDelaySeconds": 300
},
"description": "Production SMS and Voice webhooks",
"createdAt": "2026-04-07T10:00:00.000Z",
"updatedAt": "2026-04-07T10:00:00.000Z"
}
}
# Invalid operator/network combination
{
"message": "This operator and network combination does not exist",
"error": "Bad Request",
"statusCode": 400
}
# SMS not supported for this operator/network
{
"message": "This operator and network combination does not currently support SMS webhooks",
"error": "Bad Request",
"statusCode": 400
}
Sensitive fields (password, token, apiKey) are returned as ***ENCRYPTED*** in all responses. Save the registrationId to use when updating or deleting this registration.
/api/v1/webhooks/registrations
Retrieve all webhook registrations for the authenticated account. Use optional query parameters to filter results.
| Parameter | Type | Description |
|---|---|---|
| operator | string | Filter by operator name (e.g. SpenzaJ) |
| network | string | Filter by network name (e.g. SpenzaJ, T-Mobile) |
| eventType | string | Filter by sms, voice, or both |
| status | string | Filter by active, inactive, or suspended |
{
"success": true,
"message": "Webhook registrations retrieved successfully",
"data": [
{
"_id": "69146d70ed68e3890a45c228",
"customerId": "69d4e379ad169da6122101a2",
"operator": "SpenzaJ",
"network": "SpenzaJ",
"eventType": "both",
"authentication": {
"type": "basic",
"username": "webhook-user",
"password": "***ENCRYPTED***"
},
"webhookUrls": {
"messageUrl": "https://your-api.com/webhooks/sms/incoming",
"callbackMessageUrl": "https://your-api.com/webhooks/sms/status",
"voiceUrl": "https://your-api.com/webhooks/voice/incoming",
"callbackVoiceUrl": "https://your-api.com/webhooks/voice/status"
},
"retryPolicy": {
"maxAttempts": 5,
"backoffStrategy": "exponential",
"initialDelaySeconds": 5,
"maxDelaySeconds": 300
},
"status": "active",
"description": "Production SMS and Voice webhooks",
"createdAt": "2026-04-07T10:00:00.000Z",
"updatedAt": "2026-04-07T10:00:00.000Z"
}
],
"count": 1
}
Sensitive fields (password, token, apiKey) are returned as ***ENCRYPTED***. Each entry is uniquely identified by the combination of operator + network.
/api/v1/webhooks/registrations/:id
Permanently delete a webhook registration. The customer will no longer receive webhook events for the associated operator and network combination.
| Parameter | Type | Description |
|---|---|---|
| id | string (MongoId) | The registration ID (obtained from the Register or List Webhooks endpoint) |
{
"success": true,
"message": "Webhook registration deleted successfully"
}
# Registration Not Found
{
"message": "Webhook registration not found",
"error": "Not Found",
"statusCode": 404
}
# Unauthorized
{
"message": "Unauthorized",
"statusCode": 401
}
/api/v1/customer/register-sip
Register or update your SIP configuration.
{
"sipIngressIp": "203.0.113.10",
"sipPort": 5060,
"transport": "udp",
"codecs": ["ulaw", "alaw"],
"didRanges": ["+1415000xxxx", "+1818444xxxx"],
"allowedSourceIps": ["203.0.113.10", "203.0.113.11"]
}
| Field | Type | Description |
|---|---|---|
| sipIngressIp | string | Your SIP ingress IP address |
{
"message": "SIP configuration registered successfully",
"sipConfig": {
"sipIngressIp": "203.0.113.10",
"sipPort": 5060,
"transport": "udp",
"codecs": ["ulaw", "alaw"],
"didRanges": ["+1415000xxxx", "+1818444xxxx"],
"allowedSourceIps": ["203.0.113.10", "203.0.113.11"]
},
"isSIPEnabled": true
}
/api/v1/customer/register-sip
Retrieve your current SIP configuration.
{
"isSIPEnabled": true,
"sipConfig": {
"sipIngressIp": "203.0.113.10",
"sipPort": 5060,
"transport": "udp",
"codecs": ["ulaw", "alaw"],
"didRanges": ["+1415000xxxx", "+1818444xxxx"],
"allowedSourceIps": ["203.0.113.10", "203.0.113.11"]
}
}
/api/v1/customer/register-sip
Delete your SIP configuration and disable SIP.
{
"message": "SIP configuration deleted successfully"
}
The V3 API endpoints use an asynchronous pattern for long-running operations. Instead of waiting for the operation to complete, these endpoints return immediately with a transactionId that you can use to poll for status updates.
Step 1: Call any V3 endpoint (e.g., /api/v3/purchase-plan)
Step 2: Receive immediate response with transactionId and statusEndpoint
Step 3: Poll the status endpoint regularly (recommended: every 5 seconds)
Step 4: When status is COMPLETED, retrieve the final result
| Status | Description |
|---|---|
PENDING |
Transaction created, waiting to be processed |
PROCESSING |
Operation is currently being executed |
COMPLETED |
Operation finished successfully, result available |
FAILED |
Operation failed, error message available |
/api/v3/transaction/:transactionId/status
Retrieves the current status of an async transaction. This endpoint is public and does not require authentication - the transaction ID acts as the authorization token.
| Parameter | Type | Description |
|---|---|---|
| transactionId | string | The transaction ID returned from the async API call |
{
"success": true,
"transactionId": "v3_1698765432100_abc123",
"status": "PENDING",
"message": "Transaction is pending",
"timestamp": "2024-01-15T10:30:00.000Z",
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z"
}
{
"success": true,
"transactionId": "v3_1698765432100_abc123",
"status": "COMPLETED",
"message": "Transaction completed successfully",
"timestamp": "2024-01-15T10:32:00.000Z",
"result": {
"subscription": {
"subscriptionId": "sub_12345",
"status": "ACTIVE",
"planName": "Premium Plan"
}
},
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:32:00.000Z",
"processingStartedAt": "2024-01-15T10:30:01.000Z",
"processingCompletedAt": "2024-01-15T10:32:00.000Z"
}
{
"success": true,
"transactionId": "v3_1698765432100_abc123",
"status": "FAILED",
"message": "Transaction failed",
"timestamp": "2024-01-15T10:31:00.000Z",
"error": "Insufficient balance",
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:31:00.000Z"
}
Performance: Status checks hit Redis cache first (~1-2ms), falling back to MongoDB only if needed (~20-50ms). Cache has 24-hour TTL.
/api/v3/purchase-plan
Initiates an asynchronous plan purchase. Returns immediately with a transaction ID for status polling.
{
"iccid": "8901260853182965429",
"productName": "Premium_Plan_100GB",
"activateNow": true,
"scheduleDate": "2024-04-20"
}
| Field | Type | Description |
|---|---|---|
| iccid | string | The ICCID of the SIM card |
| productName | string | The name/ID of the plan to purchase |
| activateNow | boolean | Optional. Defaults to true if scheduleDate omitted, false if scheduleDate provided |
| scheduleDate | string | Optional. Date to schedule activation (YYYY-MM-DD format) |
{
"success": true,
"transactionId": "v3_1698765432100_abc123",
"statusEndpoint": "/api/v3/transaction/v3_1698765432100_abc123/status",
"message": "Transaction initiated",
"timestamp": "2024-01-15T10:30:00.000Z"
}
Timeout: 15 minutes. If the operation doesn't complete within this time, it will be marked as FAILED.
/api/v3/activate-subscription
Activates a previously purchased subscription asynchronously.
{
"iccid": "8901260853182965429",
"productId": "product_12345"
}
| Field | Type | Description |
|---|---|---|
| iccid | string | The ICCID of the SIM card |
| productId | string | The ID of the plan to activate |
{
"success": true,
"transactionId": "v3_1698765432101_def456",
"statusEndpoint": "/api/v3/transaction/v3_1698765432101_def456/status",
"message": "Activation initiated",
"timestamp": "2024-01-15T10:30:00.000Z"
}
Timeout: 15 minutes
/api/v3/cancel-subscription
Cancels an active subscription asynchronously.
{
"iccid": "8901260853182965429"
}
| Field | Type | Description |
|---|---|---|
| iccid | string | The ICCID of the SIM card with the subscription to cancel |
{
"success": true,
"transactionId": "v3_1698765432102_ghi789",
"statusEndpoint": "/api/v3/transaction/v3_1698765432102_ghi789/status",
"message": "Cancellation initiated",
"timestamp": "2024-01-15T10:30:00.000Z"
}
Timeout: 15 minutes
/api/v3/esim-purchase
Purchases and provisions an eSIM asynchronously. This operation can take up to 45 minutes.
{
"deviceId": "device_12345",
"planId": "plan_67890",
"imei": "356938035643809",
"activateNow": true
}
| Field | Type | Description |
|---|---|---|
| deviceId | string | The device identifier |
| planId | string | The ID of the plan to purchase |
| imei | string | Device IMEI number (15 digits) |
| activateNow | boolean | Whether to activate immediately |
{
"success": true,
"transactionId": "v3_1698765432103_jkl012",
"statusEndpoint": "/api/v3/transaction/v3_1698765432103_jkl012/status",
"message": "eSIM purchase initiated",
"timestamp": "2024-01-15T10:30:00.000Z"
}
Timeout: 60 minutes. eSIM provisioning can take up to 45 minutes, so this operation has an extended timeout.
/api/v3/renew-phone-number
Requests a new phone number for a SIM card asynchronously. Subject to rate limits based on your plan.
{
"iccid": "8901260853182965429"
}
| Field | Type | Description |
|---|---|---|
| iccid | string | The ICCID of the SIM card |
{
"success": true,
"transactionId": "v3_1698765432104_mno345",
"statusEndpoint": "/api/v3/transaction/v3_1698765432104_mno345/status",
"message": "Phone number renewal initiated",
"timestamp": "2024-01-15T10:30:00.000Z"
}
Monthly Rate Limits: This endpoint is subject to monthly rate limits based on your plan configuration. The limit is defined in the plan's apiLimits.renewPhoneNumber property.
Timeout: 15 minutes
| HTTP Code | Message | Description |
|---|---|---|
| 400 | Missing Fields | Required fields are missing in the request |
| 400 | Invalid API Credentials | The provided API credentials are invalid |
| 400 | Plan Not Found | The requested plan does not exist |
| 400 | Sim Not Found | The requested SIM does not exist |
| 400 | Subscription Not Found | The requested subscription does not exist |
| 429 | Rate Limit Exceeded | Monthly rate limit exceeded for API requests |
| 401 | Unauthorized | Invalid or missing authentication token |
| 403 | Forbidden | Access denied or insufficient permissions |
| 500 | Internal Server Error | Something went wrong at our end |
https://api-prod.spenza.com
# All authenticated endpoints require these headers
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
# Step 1: Get your API credentials from Spenza
# Step 2: Call /api/v1/authenticate to get access token
# Step 3: Use the token in all subsequent API calls
# Example: Authenticate first
$ curl \
-X GET 'https://api-prod.spenza.com/api/v1/authenticate' \
-H 'Content-Type: application/json' \
-d '{
"key": "your-api-key",
"secret": "your-api-secret"
}'
{
"message": "Authentication Success.",
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiration": "2024-01-16T10:30:00.000Z"
}
# Include the token in Authorization header
$ curl \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' \
-H 'Content-Type: application/json' \
'https://api-prod.spenza.com/api/v1/sim-by-iccid'
# Get your authentication token
$ curl \
-X GET 'https://api-prod.spenza.com/api/v1/authenticate' \
-H 'Content-Type: application/json' \
-d '{
"key": "api-key",
"secret": "api-secret"
}'
{
"message": "Authentication Success.",
"accessToken": "Your Auth Token for other APIs.",
"expiration": "token expiration time"
}
# Use the received token in subsequent API calls
$ curl \
-X GET 'https://api-prod.spenza.com/api/v1/sim-by-iccid' \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"ICCID": "9999967890123456789"
}'
# Missing Fields
{
"errors": [{
"message": "Api Key and Secrets are Required Fields.",
"fields": ["key", "secret"]
}]
}
# Missing field key
{
"errors": [{
"message": "Api Key is Required Field.",
"fields": ["key"]
}]
}
# Missing field secret
{
"errors": [{
"message": "Api Secret is Required Field.",
"fields": ["secret"]
}]
}
# Invalid API Credentials
{
"errors": [{
"message": "Invalid Api Credentials."
}]
}
# Internal Server Error
{
"errors": [{
"message": "Something is Wrong at our end. Our Engineers are being Notified."
}]
}
# Retrieve SIM information using ICCID
$ curl \
-X GET 'https://api-prod.spenza.com/api/v1/sim-by-iccid' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"ICCID": "9999967890123456789"
}'
{
"message": "Success.",
"simInfo": {
"status": {
"user": "Assigned",
"device": "Assigned"
},
"operator": {
"custNbr": null
},
"ICCID": "9999967890123456784",
"activationCode": "",
"source": "Other",
"isDeleted": false,
"recommendedPlan": "60af49af21647e2ca0b15f51",
"_id": "632808a8e85110002e3ae629",
"network": "T-Mobile",
"Operator": "T-Mobile",
"phoneNumber": "78993093857",
"group": "63280750e85110002e3ae624",
"account": "63280446e85110002e3ae54e",
"phoneLineStatus": "ACTIVATED"
}
}
# Missing field ICCID
{
"errors": [{
"message": "ICCID is required field.",
"field": "ICCID"
}]
}
# Internal Server Error
{
"errors": [{
"message": "Something is Wrong at our end. Our Engineers are being Notified."
}]
}
# Retrieve plan information using planId
$ curl \
-X GET 'https://api-prod.spenza.com/api/v1/plan-by-id' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"planId": "AT0001"
}'
{
"message": "Success.",
"plan": {
"stripe": {
"price": "price_1LgUXYAq7JKAyJICWrRooGBI"
},
"productName": "AT&T100",
"category": "PLANS",
"Operator": "AT&T",
"network": "AT&T",
"zone": "US",
"validityDays": 30,
"dataMB": 10,
"speed": "4G",
"price": 10,
"inStock": 0,
"durationType": "Monthly",
"isDeleted": false,
"_id": "63184df1203d4994052869f7",
"description": "AT&T Plan 1",
"productId": "AT0001",
"feature": []
}
}
# Missing field planId
{
"errors": [{
"message": "Plan Id is Required.",
"field": "planId"
}]
}
# Plan Not Found
{
"errors": [{
"message": "Plan Not Found."
}]
}
# Internal Server Error
{
"errors": [{
"message": "Something is Wrong at our end. Our Engineers are being Notified."
}]
}
# Purchase a plan using ICCID, productId and schedule info
$ curl \
-X POST 'https://api-prod.spenza.com/api/v1/purchase-plan' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"ICCID": "8901260853182965429",
"productId": "product1",
"activateNow": true,
"scheduledDate": "2024-04-20"
}'
{
"message": "Success.",
"subscription": {
"stripe": {
"subId": "sub_1LkKThAq7JKDyJICNJ3l1WWv",
"scheduleId": null,
"price": null
},
"operatorRes": {
"MDN": null,
"description": null
},
"planName": "EXPLAN_100",
"planCode": "EXPLAN_001",
"status": "ACTIVE",
"initiatedDate": "2022-08-21T04:24:06.473Z",
"startDate": "2022-08-21T04:24:05.000Z",
"endDate": "2022-11-21T04:24:05.000Z",
"scheduledDate": "2022-09-20T00:00:00.000Z",
"cancelledDate": null,
"autoTopUp": true,
"subId": "1663734246474",
"Operator": "T-Mobile",
"network": "T-Mobile",
"orderId": "1739-597673-2114"
}
}
# Missing field ICCID
{
"errors": [{
"message": "ICCID Key is Required Field.",
"fields": ["ICCID"]
}]
}
# Missing field productId
{
"errors": [{
"message": "productId is Required Field.",
"fields": ["productId"]
}]
}
# Missing field activateNow
{
"errors": [{
"message": "activateNow is Required Field.",
"fields": ["activateNow"]
}]
}
# Missing field scheduledDate
{
"errors": [{
"message": "scheduledDate is Required Field. Format:(YYYY-MM-DD)",
"fields": ["scheduledDate"]
}]
}
# Plan Not Found
{
"errors": [{
"message": "Plan Not Found."
}]
}
# Operator Mismatch
{
"errors": [{
"message": "Sim Is Not Compatible. Please check Plan Operator and Sim Operator."
}]
}
# Active Subscription Exists
{
"errors": [{
"message": "Sim Already has an Active Subscription."
}]
}
# Internal Server Error
{
"errors": [{
"message": "Something Went Wrong at our End. Our Engineers are being Notified about this Error."
}]
}
# Activate a purchased plan
$ curl \
-X PUT 'https://api-prod.spenza.com/api/v1/activate-subscription' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"ICCID": "8901260853182965429",
"productId": "product1"
}'
{
"message": "Success",
"subscription": {
"stripe": {
"subId": "sub_1LkKThAq7JKDyJICNJ3l1WWv",
"scheduleId": null,
"price": null
},
"operatorRes": {
"MDN": null,
"description": null
},
"planName": "EXPLAN_101",
"planCode": "EXPLAN_003",
"status": "ACTIVE",
"initiatedDate": "2022-08-21T04:24:06.473Z",
"startDate": "2022-08-21T04:24:05.000Z",
"endDate": "2022-11-21T04:24:05.000Z",
"scheduledDate": "2022-09-20T00:00:00.000Z",
"autoTopUp": true,
"subId": "1663734246474",
"Operator": "T-Mobile",
"network": "T-Mobile",
"orderId": "1739-597673-2114"
}
}
# Missing field ICCID
{
"errors": [{
"message": "ICCID is Required Field.",
"fields": ["ICCID"]
}]
}
# Missing field productId
{
"errors": [{
"message": "productId is Required Field.",
"fields": ["productId"]
}]
}
# Plan Not Found
{
"errors": [{
"message": "Plan Not Found."
}]
}
# Sim Not Found
{
"errors": [{
"message": "Sim Not Found."
}]
}
# Subscription Not Found
{
"errors": [{
"message": "Subscription Not Found."
}]
}
# Internal Server Error
{
"errors": [{
"message": "Something is Wrong at our end. Our Engineers are being Notified."
}]
}
# Cancel an active subscription
$ curl \
-X POST 'https://api-prod.spenza.com/api/v1/cancel-subscription' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"ICCID": "9999967890123456789"
}'
{
"message": "Subscription Cancelled.",
"subscription": {
"stripe": {
"subId": "sub_1LkKThAq7JKDyJICNJ3l1WWv",
"scheduleId": null,
"price": null
},
"operatorRes": {
"MDN": null,
"description": null
},
"planName": "EXPLAN_100",
"planCode": "EXPLAN_001",
"status": "CANCELLED",
"initiatedDate": "2022-08-21T04:24:06.473Z",
"startDate": "2022-08-21T04:24:05.000Z",
"endDate": "2022-11-21T04:24:05.000Z",
"scheduledDate": "2022-09-20T00:00:00.000Z",
"autoTopUp": true,
"subId": "1663734246474",
"Operator": "T-Mobile",
"network": "T-Mobile",
"orderId": "1739-597673-2114"
}
}
# Missing field ICCID
{
"errors": [{
"message": "ICCID is required field.",
"fields": ["ICCID"]
}]
}
# Sim Not Found
{
"errors": [{
"message": "Sim Not Found."
}]
}
# Subscription Not Found
{
"errors": [{
"message": "Subscription Not Found."
}]
}
# Internal Server Error
{
"errors": [{
"message": "Something is Wrong at our end. Our Engineers are being Notified."
}]
}
# Retrieve subscription by ICCID
$ curl \
-X GET 'https://api-prod.spenza.com/api/v1/subscription-by-iccid' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"ICCID": "9999967890123456789"
}'
{
"message": "Success.",
"subscription": {
"stripe": {
"subId": "sub_1LkKThAq7JKAyJICNJ3l1WWv",
"scheduleId": null,
"price": null
},
"operatorRes": {
"MDN": null,
"description": null
},
"planName": "T-Mobile100",
"planCode": "T-Mobile0001",
"status": "Pending",
"initiatedDate": "2022-09-21T04:24:06.473Z",
"startDate": "2022-09-21T04:24:05.000Z",
"endDate": "2022-10-21T04:24:05.000Z",
"scheduledDate": "2022-09-20T00:00:00.000Z",
"cancelledDate": null,
"autoTopUp": true,
"subId": "1663734246473",
"Operator": "T-Mobile",
"network": "T-Mobile",
"sim": "632a916b961e53002e1298d7",
"account": "63280446e85110002e3ae54e",
"orderId": "1339-597673-2114"
}
}
# Missing field ICCID
{
"errors": [{
"message": "ICCID is required field.",
"field": "ICCID"
}]
}
# Sim Not Found
{
"errors": [{
"message": "Sim Not Found."
}]
}
# Subscription Not Found
{
"errors": [{
"message": "Subscription Not Found."
}]
}
# Internal Server Error
{
"errors": [{
"message": "Something Went Wrong at our End. Our Engineers are being Notified about this Error."
}]
}
# Retrieve the usage of a Sim
$ curl \
-X POST 'https://api-prod.spenza.com/api/v1/telco/getUsage' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"subId": "1745863440782"
}'
{
"result": {
"usageData": 0,
"usageCalls": 0,
"usageSms": 0,
"usageUpdatedAt": 1747223324287,
"telcoStatus": "ACTIVATED",
"usageDataConverted": {
"format": "B",
"value": 0
},
"network": "Verizon",
"networkCode": "Verizon"
}
}
# Missing field subId
{
"errors": [{
"message": "Subscription Id is a Required Field.",
"fields": ["subId"]
}]
}
# No Active Invoice
{
"errors": [{
"message": "Subscription Don't have any Active Invoices."
}]
}
# Internal Server Error
{
"errors": [{
"message": "Something is Wrong at our end. Our Engineers are being Notified."
}]
}
# Request a new phone number for a SIM card
$ curl \
-X POST 'https://api-prod.spenza.com/api/v1/renew-phone-number' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"ICCID": "9999967890123456789"
}'
{
"message": "Request placed successfully, new number will be assigned soon"
}
# Missing field ICCID
{
"errors": [{
"message": "ICCID is required field.",
"fields": ["ICCID"]
}]
}
# Subscription Not Found
{
"errors": [{
"message": "Subscription Not Found. Please Create an Active Subscription First."
}]
}
# Rate Limit Exceeded
{
"errors": [{
"message": "Monthly rate limit exceeded for renew phone number requests."
}]
}
# Internal Server Error
{
"error": "Something went wrong"
}
# Validate ICCID format (no auth required)
$ curl \
-X POST 'https://api-prod.spenza.com/api/v1/public/validate-iccid' \
-H 'Content-Type: application/json' \
-d '{
"ICCID": "8901260853182965429"
}'
{
"isValid": true,
"message": "Valid ICCID."
}
# Validate IMEI format (no auth required)
$ curl \
-X POST 'https://api-prod.spenza.com/api/v1/public/validate-imei' \
-H 'Content-Type: application/json' \
-d '{
"IMEI": "356938035643809"
}'
{
"isValid": true,
"message": "Valid IMEI."
}
# Purchase eSIM with IMEI and SIM ID only
$ curl \
-X POST 'https://api-prod.spenza.com/api/v1/purchase-esim' \
-H 'Authorization: Bearer YOUR_JWT_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"imei": "451014850281267",
"simId": "TEST_SPENZA"
}'
# Purchase eSIM with user information for number provisioning
$ curl \
-X POST 'https://api-prod.spenza.com/api/v1/purchase-esim' \
-H 'Authorization: Bearer YOUR_JWT_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"imei": "451014850281267",
"simId": "SpenzaJ_ESIM",
"user": {
"name": "Testung",
"email": "test+8@spenza.com",
"phoneNumber": "2031134322",
"address": "47 W 13th St",
"city": "New York",
"state": "New York",
"zipcode": "10011"
}
}'
{
"requestId": "64dfa874-f157-431a-a6db-d8e5a915ea12",
"status": "PENDING",
"message": "eSIM purchase initiated successfully"
}
# After successful eSIM purchase:
# 1. Subscription is created with PENDING status
# 2. Phone number is provisioned based on the user's ZIP code location
# 3. eSIM QR code is generated and sent to customer via email
# 4. Customer can activate eSIM by scanning the QR code
# Poll for purchase status
$ curl \
-X GET 'https://api-prod.spenza.com/api/v1/purchase-esim/550e8400-e29b-41d4-a716-446655440000' \
-H 'Authorization: Bearer YOUR_JWT_TOKEN'
{
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"status": "SUCCESS",
"result": {
"mdn": "1234567890",
"iccid": "89012345678901234567",
"qrCode": "https://s3.amazonaws.com/esim-activation-code/customer-id/iccid-esim-activation-qrcode.png"
},
"createdAt": "2024-12-05T10:00:00.000Z",
"updatedAt": "2024-12-05T10:00:45.000Z"
}
# Register webhook for SMS and Voice events
$ curl \
-X POST 'https://api-prod.spenza.com/api/v1/webhooks/register' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"operator": "SpenzaJ",
"network": "SpenzaJ",
"eventType": "both",
"authentication": {
"type": "basic",
"username": "webhook-user",
"password": "super-secret"
},
"webhookUrls": {
"messageUrl": "https://your-api.com/webhooks/sms/incoming",
"callbackMessageUrl": "https://your-api.com/webhooks/sms/status",
"voiceUrl": "https://your-api.com/webhooks/voice/incoming",
"callbackVoiceUrl": "https://your-api.com/webhooks/voice/status"
},
"retryPolicy": {
"maxAttempts": 5,
"backoffStrategy": "exponential",
"initialDelaySeconds": 5,
"maxDelaySeconds": 300
},
"status": "active",
"description": "Production SMS and Voice webhooks"
}'
{
"success": true,
"message": "Webhook registration created successfully",
"data": {
"registrationId": "69146d70ed68e3890a45c228",
"operator": "SpenzaJ",
"network": "SpenzaJ",
"eventType": "both",
"status": "active",
"authentication": {
"type": "basic",
"username": "webhook-user",
"password": "***ENCRYPTED***"
},
"webhookUrls": {
"messageUrl": "https://your-api.com/webhooks/sms/incoming",
"callbackMessageUrl": "https://your-api.com/webhooks/sms/status",
"voiceUrl": "https://your-api.com/webhooks/voice/incoming",
"callbackVoiceUrl": "https://your-api.com/webhooks/voice/status"
},
"retryPolicy": {
"maxAttempts": 5,
"backoffStrategy": "exponential",
"initialDelaySeconds": 5,
"maxDelaySeconds": 300
},
"description": "Production SMS and Voice webhooks",
"createdAt": "2026-04-07T10:00:00.000Z",
"updatedAt": "2026-04-07T10:00:00.000Z"
}
}
Sensitive fields are returned as ***ENCRYPTED***. If a registration already exists for the same operator + network, it will be updated. Save the registrationId to update or delete this registration later.
# List registered webhooks (with optional filters)
$ curl \
-X GET 'https://api-prod.spenza.com/api/v1/webhooks/registrations?operator=SpenzaJ&status=active' \
-H 'Authorization: Bearer YOUR_TOKEN'
{
"success": true,
"message": "Webhook registrations retrieved successfully",
"data": [
{
"_id": "69146d70ed68e3890a45c228",
"customerId": "69d4e379ad169da6122101a2",
"operator": "SpenzaJ",
"network": "SpenzaJ",
"eventType": "both",
"authentication": {
"type": "basic",
"username": "webhook-user",
"password": "***ENCRYPTED***"
},
"webhookUrls": {
"messageUrl": "https://your-api.com/webhooks/sms/incoming",
"callbackMessageUrl": "https://your-api.com/webhooks/sms/status",
"voiceUrl": "https://your-api.com/webhooks/voice/incoming",
"callbackVoiceUrl": "https://your-api.com/webhooks/voice/status"
},
"retryPolicy": {
"maxAttempts": 5,
"backoffStrategy": "exponential",
"initialDelaySeconds": 5,
"maxDelaySeconds": 300
},
"status": "active",
"description": "Production SMS and Voice webhooks",
"createdAt": "2026-04-07T10:00:00.000Z",
"updatedAt": "2026-04-07T10:00:00.000Z"
}
],
"count": 1
}
Filter by operator, network, eventType, or status using query parameters.
# Delete webhook registration by ID
$ curl \
-X DELETE 'https://api-prod.spenza.com/api/v1/webhooks/registrations/69146d70ed68e3890a45c228' \
-H 'Authorization: Bearer YOUR_TOKEN'
{
"success": true,
"message": "Webhook registration deleted successfully"
}
# Registration Not Found
{
"message": "Webhook registration not found",
"error": "Not Found",
"statusCode": 404
}
# Unauthorized
{
"error": "Unauthorized"
}
# Register or update SIP configuration
$ curl \
-X POST 'https://api-prod.spenza.com/api/v1/customer/register-sip' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"sipIngressIp": "203.0.113.10",
"sipPort": 5060,
"transport": "udp",
"codecs": ["ulaw", "alaw"],
"didRanges": ["+1415000xxxx", "+1818444xxxx"],
"allowedSourceIps": ["203.0.113.10", "203.0.113.11"]
}'
{
"message": "SIP configuration registered successfully",
"sipConfig": {
"sipIngressIp": "203.0.113.10",
"sipPort": 5060,
"transport": "udp",
"codecs": ["ulaw", "alaw"],
"didRanges": ["+1415000xxxx", "+1818444xxxx"],
"allowedSourceIps": ["203.0.113.10", "203.0.113.11"]
},
"isSIPEnabled": true
}
# Retrieve your SIP configuration
$ curl \
-X GET 'https://api-prod.spenza.com/api/v1/customer/register-sip' \
-H 'Authorization: Bearer YOUR_TOKEN'
{
"isSIPEnabled": true,
"sipConfig": {
"sipIngressIp": "203.0.113.10",
"sipPort": 5060,
"transport": "udp",
"codecs": ["ulaw", "alaw"],
"didRanges": ["+1415000xxxx", "+1818444xxxx"],
"allowedSourceIps": ["203.0.113.10", "203.0.113.11"]
}
}
# Delete SIP configuration and disable SIP
$ curl \
-X DELETE 'https://api-prod.spenza.com/api/v1/customer/register-sip' \
-H 'Authorization: Bearer YOUR_TOKEN'
{
"message": "SIP configuration deleted successfully"
}
# Step 1: Initiate async operation
$ curl \
-X POST 'https://api-prod.spenza.com/api/v3/purchase-plan' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"iccid": "8901260853182965429",
"productName": "Premium_Plan",
"activateNow": true
}'
{
"success": true,
"transactionId": "v3_1698765432100_abc123",
"statusEndpoint": "/api/v3/transaction/v3_1698765432100_abc123/status",
"message": "Transaction initiated",
"timestamp": "2024-01-15T10:30:00.000Z"
}
$ curl \
'https://api-prod.spenza.com/api/v3/transaction/v3_1698765432100_abc123/status'
{
"success": true,
"transactionId": "v3_1698765432100_abc123",
"status": "COMPLETED",
"result": {
"subscription": {
"subscriptionId": "sub_12345",
"status": "ACTIVE"
}
},
"timestamp": "2024-01-15T10:32:00.000Z"
}
# Check transaction status (no auth required)
$ curl \
'https://api-prod.spenza.com/api/v3/transaction/v3_1698765432100_abc123/status'
{
"success": true,
"transactionId": "v3_1698765432100_abc123",
"status": "PENDING",
"message": "Transaction is pending",
"timestamp": "2024-01-15T10:30:00.000Z"
}
{
"success": true,
"transactionId": "v3_1698765432100_abc123",
"status": "COMPLETED",
"result": {
"subscription": {
"subscriptionId": "sub_12345",
"status": "ACTIVE"
}
},
"processingStartedAt": "2024-01-15T10:30:01.000Z",
"processingCompletedAt": "2024-01-15T10:32:00.000Z"
}
{
"success": true,
"transactionId": "v3_1698765432100_abc123",
"status": "FAILED",
"error": "Insufficient balance",
"timestamp": "2024-01-15T10:31:00.000Z"
}
# Initiate async plan purchase
$ curl \
-X POST 'https://api-prod.spenza.com/api/v3/purchase-plan' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"iccid": "8901260853182965429",
"productName": "Premium_Plan_100GB",
"activateNow": true
}'
{
"success": true,
"transactionId": "v3_1698765432100_abc123",
"statusEndpoint": "/api/v3/transaction/v3_1698765432100_abc123/status",
"message": "Transaction initiated",
"timestamp": "2024-01-15T10:30:00.000Z"
}
$ curl \
-X POST 'https://api-prod.spenza.com/api/v3/purchase-plan' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"iccid": "8901260853182965429",
"productName": "Premium_Plan",
"scheduleDate": "2024-04-20"
}'
# Activate subscription asynchronously
$ curl \
-X PUT 'https://api-prod.spenza.com/api/v3/activate-subscription' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"iccid": "8901260853182965429",
"productId": "product_12345"
}'
{
"success": true,
"transactionId": "v3_1698765432101_def456",
"statusEndpoint": "/api/v3/transaction/v3_1698765432101_def456/status",
"message": "Activation initiated",
"timestamp": "2024-01-15T10:30:00.000Z"
}
# Cancel subscription asynchronously
$ curl \
-X POST 'https://api-prod.spenza.com/api/v3/cancel-subscription' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"iccid": "8901260853182965429"
}'
{
"success": true,
"transactionId": "v3_1698765432102_ghi789",
"statusEndpoint": "/api/v3/transaction/v3_1698765432102_ghi789/status",
"message": "Cancellation initiated",
"timestamp": "2024-01-15T10:30:00.000Z"
}
# Initiate eSIM purchase (can take up to 45 minutes)
$ curl \
-X POST 'https://api-prod.spenza.com/api/v3/esim-purchase' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"deviceId": "device_12345",
"planId": "plan_67890",
"imei": "356938035643809",
"activateNow": true
}'
{
"success": true,
"transactionId": "v3_1698765432103_jkl012",
"statusEndpoint": "/api/v3/transaction/v3_1698765432103_jkl012/status",
"message": "eSIM purchase initiated",
"timestamp": "2024-01-15T10:30:00.000Z"
}
{
"success": true,
"transactionId": "v3_1698765432103_jkl012",
"status": "COMPLETED",
"result": {
"mdn": "1234567890",
"iccid": "89012345678901234567",
"qrCode": "https://s3.amazonaws.com/esim-qr-codes/abc123.png"
}
}
# Request new phone number asynchronously
$ curl \
-X POST 'https://api-prod.spenza.com/api/v3/renew-phone-number' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"iccid": "8901260853182965429"
}'
{
"success": true,
"transactionId": "v3_1698765432104_mno345",
"statusEndpoint": "/api/v3/transaction/v3_1698765432104_mno345/status",
"message": "Phone number renewal initiated",
"timestamp": "2024-01-15T10:30:00.000Z"
}
{
"success": true,
"transactionId": "v3_1698765432104_mno345",
"status": "COMPLETED",
"result": {
"newPhoneNumber": "+15551234567",
"iccid": "8901260853182965429"
}
}
{
"errors": [{
"message": "Missing required field: ICCID",
"fields": ["ICCID"]
}]
}
{
"statusCode": 401,
"message": "Invalid or expired authentication token",
"error": "Unauthorized"
}
{
"statusCode": 403,
"message": "Access denied. Insufficient permissions.",
"error": "Forbidden"
}
{
"statusCode": 404,
"message": "Resource not found",
"error": "Not Found"
}
{
"statusCode": 429,
"message": "Monthly rate limit exceeded for this operation",
"error": "Too Many Requests",
"retryAfter": "2024-02-01T00:00:00.000Z"
}
{
"statusCode": 500,
"message": "Something went wrong at our end. Our engineers are being notified.",
"error": "Internal Server Error"
}
# Always check the HTTP status code
$ curl \
-w '\nHTTP Status: %{http_code}\n' \
-X GET 'https://api-prod.spenza.com/api/v1/sim-by-iccid' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"ICCID": "8901260853182965429"
}'