Charges
Calculate the exact payable amount for an eSIM plan before placing the order
POST /api/v1/esim/charges
Resolves the matching variant for the given product_id + amount and returns the full price breakdown — same shape as the topup charges and voucher charges endpoints, so a single client implementation works across all three verticals.
eSIM orders are always one-at-a-time. There is no quantity in the request and no max_quantity in the response. eSIM plans are fixed-denomination, so amount must match one of the values exposed on /esim/products/:id/variants.
Cross-currency wallets are supported. Pass wallet_id to bill a specific wallet; when its currency differs from the variant's, Octopus Cards looks up an admin-managed forex rate and the response carries net_amount / handling_fee_amount / forex_rate / conversion_fee in the wallet currency. If wallet_id is omitted, Octopus Cards picks a wallet in the variant currency, falling back to your default-currency wallet.
Request
curl -X POST "https://api.octopuscards.io/api/v1/esim/charges" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"product_id": 712,
"amount": 4.50
}'Request Parameters
| Key | Type | Required | Description |
|---|---|---|---|
product_id | integer | Yes | eSIM product to price. Must be > 0. |
amount | number | Yes | Variant price in the product's currency. Must match an amount value from /esim/products/:id/variants — eSIM plans are fixed-denomination. |
wallet_id | integer | No | Wallet to bill. When supplied and its currency differs from the variant's, the response is FX-converted into the wallet currency. Omit to use the variant-currency wallet (or your default-currency wallet as fallback). |
There is no quantity field — eSIM orders are always 1 per order.
Response
Same-currency wallet (no FX):
{
"non_discounted_total": 4.50,
"discount_amount": 0.225,
"total_amount": 4.275,
"discount": 5.0,
"total_payable": 4.275,
"charges_details": {
"source_currency": "USD",
"destination_currency": "USD"
}
}Cross-currency wallet (e.g. USD variant billed to an INR wallet):
{
"non_discounted_total": 4.50,
"discount_amount": 0.225,
"total_amount": 4.275,
"discount": 5.0,
"net_amount": 357.0,
"handling_fee_amount": 0.0,
"total_payable": 357.0,
"charges_details": {
"source_currency": "USD",
"destination_currency": "INR",
"forex_rate": 83.51,
"conversion_fee": 0.0
}
}Response Fields
| Key | Type | Description |
|---|---|---|
non_discounted_total | number | Variant amount before discount, in source_currency. |
discount_amount | number | Absolute discount applied, in source_currency. |
total_amount | number | Post-discount net in source_currency (non_discounted_total − discount_amount). |
discount | number | Client discount percentage (e.g. 5.0 means 5%). Configured per client/variant on client_esim_variants; defaults to 0 when no mapping exists. |
net_amount | number, omitempty | Present only when source ≠ destination currency. total_amount × forex_rate — the post-FX amount before the conversion fee. |
handling_fee_amount | number, omitempty | Present only when source ≠ destination currency. net_amount × conversion_fee%. Currently always 0 (placeholder). |
total_payable | number | Final amount you'll be charged in destination_currency. Equals total_amount when no FX, or net_amount + handling_fee_amount when cross-currency. |
charges_details.source_currency | string | ISO 4217 currency of the variant. |
charges_details.destination_currency | string | ISO 4217 currency you'll be billed in. Equals source_currency when no FX. |
charges_details.forex_rate | number, omitempty | Admin-managed rate used for the conversion (source → destination). Present only when source ≠ destination. |
charges_details.conversion_fee | number, omitempty | Conversion-fee percentage applied on top of FX. Present only when source ≠ destination. Currently 0. |
No max_quantity — eSIM orders are 1-at-a-time. Variant-routing internals are intentionally not surfaced.
Cross-currency FX requires an admin-managed rate in the forex_values table for the source → destination pair. If none exists, the request rejects with 400 "Exchange rate not available for the wallet currency".
Errors
400 Bad Request — product_id or amount missing or invalid.
{
"error": {
"name": "ValidationException",
"code": "VALIDATION_FAILURE",
"message": "Product ID is required"
}
}Other validation messages: "Amount is required".