# Carrier REST API

Carrier REST API 1.0.0.
Generated from the OpenAPI builder. The live copy is https://api.carrier.llc/openapi.json.
Do not edit this file by hand.

Send `Authorization: Bearer <key>` on every call except the public spec.

## GET /api/hosted/{slug}

Operation: `get_hosted_storefront`

Published hosted storefront, including its sellable plans

## GET /api/hosted/{slug}/plans/{planId}/readiness

Operation: `get_hosted_storefront_plan_readiness`

Read-only readiness check using the fulfilment owner's account rules

## GET /api/orders

Operation: `list_storefront_buyer_orders`

List eSIMs one buyer paid for on one shop

Requires CARRIER_STOREFRONT_API_KEY as a bearer token. The shop checks Clerk, then calls this with the buyer's email and the published storefront slug. An email alone is not a lookup.

## GET /api/orders/{orderId}

Operation: `get_storefront_order`

Read one storefront order

The body is the order object itself. When CARRIER_STOREFRONT_API_KEY is set, the caller must send it as a bearer token. When it is unset, the route is open and the order id is the only secret.

## DELETE /api/orders/{orderId}

Operation: `rollback_storefront_order`

Cancel one storefront order and release its eSIM (CAR-264 rollback)

Requires CARRIER_STOREFRONT_API_KEY as a bearer token — unlike the read-only lookup on this same path, this is a destructive write. Mirrors DELETE /v1/subscribers/issue-from-inventory/{idempotencyKey} on the operator channel against the same esim_issuances ledger row, keyed by storefrontIdempotencyKey(orderId), so either channel can release an eSIM it issued without a reconciliation job needing to know which one provisioned it. Only orders in status "ready" can be rolled back. The order and issuance-ledger transitions use separate conditional claims; they are not an atomic transaction or a provider delivery guarantee. Confirmed release and an unknown provider outcome are distinct. An ambiguous release requires manual reconciliation and must not automatically reissue.

## POST /api/orders/{orderId}/cancel

Operation: `cancel_storefront_order`

Record the buyer's cancel

Records the cancel so the order accepts no further top-up. It refunds nothing and changes nothing at the network. A second call returns the same time. Releasing the eSIM is the separate operator rollback on DELETE /api/orders/{orderId}.

## POST /api/orders/{orderId}/top-up-checkouts/{sessionId}/cancel

Operation: `cancel_storefront_top_up_checkout`

Cancel an outstanding top-up checkout for this order

Expires one open, unpaid top-up Checkout Session recorded against this order. A session that already reached paid is never expired here and returns 409 so a person reconciles it. Fails closed (502) when the payment state cannot be verified.

## GET /api/orders/{orderId}/top-up-options

Operation: `get_storefront_order_top_up_options`

Plans the buyer can add to this order's eSIM

The shop's own sellable plans. An empty array means nothing can be added, including when the order is cancelled.

## GET /api/orders/{orderId}/usage

Operation: `get_storefront_order_usage`

Data used by the order's own eSIM

Reads usage for the ICCID stored on this order, never for an ICCID from the request. usedBytes is the total since the order was created. An OCS failure is 502, never a 200 with usedBytes 0.

## GET /api/templates

Operation: `list_storefront_templates`

Sellable storefront plans, as a bare array

Public. An operator with no curated plans gets an empty array. The storefront treats a non-array as an upstream failure.

## GET /health

Operation: `health_check`

Health check — returns build metadata

## GET /v1/accounts

Operation: `list_reseller_accounts`

List all reseller sub-accounts

## PUT /v1/accounts/{accountId}/balance

Operation: `modify_account_balance`

Modify account-level balance (admin only)

## GET /v1/accounts/{accountId}/esim-status

Operation: `esim_status_per_account`

Get eSIM provisioning status counts for an account

## POST /v1/accounts/{accountId}/move-subscribers

Operation: `move_subscriber_range_to_account`

Move a subscriber range to a different account

## POST /v1/affiliates/link

Operation: `affiliates_link`

Attach a referral code to an organization created less than 30 minutes ago (browser sign-up only)

## GET /v1/affiliates/me

Operation: `affiliates_me_get`

Get this org's affiliate link, referral count, earnings and payout status

## POST /v1/affiliates/me

Operation: `affiliates_me_create`

Join the affiliate programme (idempotent: returns the existing code)

## GET /v1/affiliates/me/commissions

Operation: `affiliates_me_commissions_list`

List commissions for this org, newest period first

## POST /v1/ai/chat

Operation: `ai_chat`

Ask the assistant about your fleet. Streams the answer.

Server-Sent Events. `event: quota` carries the allowance after this call, `event: delta` frames carry `{ text }`, and one `event: done` ends the stream. `event: error` means the answer stopped early; the allowance is still spent, because it is reserved before the model runs.

## POST /v1/ai/insights

Operation: `ai_insights`

Generated insight cards for the caller's fleet

Always 200. `generated` is `"ai"` when a model wrote the cards and `"measured"` when it did not — no credentials, allowance spent, or upstream failure — in which case the cards are the measured figures. Clients must show the difference: the two are different claims.

## GET /v1/ai/quota

Operation: `ai_quota`

AI allowance remaining. Reads without consuming.

`available` is whether the deployment can call a model at all; `remaining` is how much of this tier's allowance is left. They are separate because "AI is off here" and "you are out of calls" are different facts and need different words.

## POST /v1/api-keys

Operation: `create_api_key`

Mint an ak_ API key for this organization (secret returned once)

Creates a Clerk API key scoped to the caller's organization, or to the caller when there is no org context. The secret is present only in this response and cannot be retrieved again. Carrier resolves read/write/admin from tier and org membership, so the key carries no caller-supplied scopes.

## GET /v1/cdr/aggregate

Operation: `aggregate_cdr`

Sum CDR bytes for one month, grouped by ICCID

At most 1000 ICCIDs, largest total first. Pass iccid to read one subscriber.

## POST /v1/cdr/ingest

Operation: `ingest_cdr_events`

Store a batch of CDR events for the caller's org

Insert OR IGNORE on event id. A repeated id counts as skipped. At most 1000 events.

## GET /v1/connectivity/events

Operation: `list_connectivity_events`

List recent connectivity events for this account

Newest first, at most 20. Pass intent to keep only events for that connectivity intent.

## GET /v1/connectivity/events/{id}

Operation: `get_connectivity_event`

Retrieve one connectivity event

## POST /v1/connectivity/events/{id}/retry

Operation: `retry_connectivity_event`

Re-sign and POST an existing event. Does not create a second event.

## POST /v1/connectivity/events/observe

Operation: `observe_connectivity_event`

Record a bridge network event against the intent that owns the ICCID

Called by the ocs-bridge. Authorization is CARRIER_INTERNAL_API_KEY, not a Clerk session. An unknown bridge_type is ignored with 200.

## GET /v1/connectivity/intents

Operation: `list_connectivity_intents`

List connectivity intents for this account

Newest first. The default page is 20. limit is 1–100.

## POST /v1/connectivity/intents

Operation: `create_connectivity_intent`

Issue one eSIM, or leave the intent waiting for a plan

Idempotency-Key is required. The same key and body return the original intent and do not draw a second SIM. Plans prefixed test_ (test_global_1gb, test_eu_1gb) succeed immediately, set livemode false, and never call the network — including on the free tier. A product id (cnp_…) is a plan this account defined. Any other id is a live catalogue slug. Live plans draw one eSIM from the caller's own account. The price on a product is not charged by this call.

## GET /v1/connectivity/intents/{id}

Operation: `get_connectivity_intent`

Retrieve one connectivity intent

## POST /v1/connectivity/intents/{id}/confirm

Operation: `confirm_connectivity_intent`

Attach a plan and issue the eSIM

## GET /v1/connectivity/plans

Operation: `list_connectivity_plans`

List test plans and live plans available to this account

## GET /v1/connectivity/products

Operation: `list_connectivity_products`

List connectivity products this account defined

Newest first, up to 100. These are connectivity.plan objects. Defining one does not debit a wallet.

## POST /v1/connectivity/products

Operation: `create_connectivity_product`

Define a connectivity plan for this account

Idempotency-Key is required. The same key and body return the original plan. Countries, allowance, and price are what the operator writes; this call does not look up coverage and does not debit a wallet. package_template_id is the OCS template used later, when an intent is issued from the caller's own account. The write scope is required.

## GET /v1/connectivity/products/{id}

Operation: `get_connectivity_product`

Retrieve one connectivity product

## POST /v1/connectivity/usage_records

Operation: `create_connectivity_usage_record`

Record usage on a test intent. Live intents are metered by the network.

Idempotency-Key is required. The same key and quantity return the original record. A live intent returns network_metered and is not stored: the prepaid wallet is already debited by the metering cron, and a second report would charge the same bytes twice.

## GET /v1/connectivity/usage_records

Operation: `list_connectivity_usage_records`

List usage records for one intent

## POST /v1/connectivity/usage_records/ingest

Operation: `ingest_connectivity_usage`

Ingest one metered usage line from the bridge

Called by the ocs-bridge. Authorization is CARRIER_INTERNAL_API_KEY, not a Clerk session. ref is the idempotency key.

## POST /v1/connectivity/webhook_endpoints

Operation: `create_connectivity_webhook_endpoint`

Register an HTTPS endpoint. The signing secret is returned once.

## GET /v1/connectivity/webhook_endpoints

Operation: `list_connectivity_webhook_endpoints`

List webhook endpoints. Secrets are omitted.

## DELETE /v1/connectivity/webhook_endpoints/{id}

Operation: `delete_connectivity_webhook_endpoint`

Remove a webhook endpoint

## GET /v1/destination-lists

Operation: `list_destination_lists`

List destination lists with full prefix detail

## GET /v1/destination-prefixes

Operation: `list_destination_prefixes`

List destination prefixes for routing configuration

## POST /v1/intelligence/audit-network-coverage

Operation: `audit_network_coverage`

Audit network coverage against location zones

## POST /v1/intelligence/churn-risk

Operation: `churn_risk`

Identify subscribers at churn risk based on usage patterns

## POST /v1/intelligence/detect-country-entry

Operation: `detect_country_entry`

Resolve a subscriber's current country from networkInfo.lastMcc and diff it against expectedCountry

## POST /v1/intelligence/detect-usage-anomalies

Operation: `detect_usage_anomalies`

Detect unusual usage spikes or drops across the fleet

## POST /v1/intelligence/diagnose-subscriber

Operation: `diagnose_subscriber`

Deep-dive diagnostic for a single subscriber

## POST /v1/intelligence/fleet-health

Operation: `fleet_health`

Aggregate fleet health: active/suspended/inactive counts, low balance alerts

## POST /v1/intelligence/high-cost-subscribers

Operation: `high_cost_subscribers`

Identify highest-cost subscribers for cost optimization

## GET /v1/intelligence/high-cost-subscribers

Operation: `high_cost_subscribers_method_guard`

GET is not supported. Use POST.

Answers 405. The query is POST /v1/intelligence/high-cost-subscribers with a JSON body of start, end, and limit. A threshold query parameter is not accepted.

## POST /v1/intelligence/marketing-intelligence

Operation: `marketing_intelligence`

Segment subscribers for targeted campaigns

## POST /v1/intelligence/optimize-package

Operation: `optimize_package`

Recommend optimal package based on subscriber usage history

## GET /v1/intelligence/schedules

Operation: `list_intelligence_schedules`

List scheduled intelligence reports for the caller's organization

## POST /v1/intelligence/schedules

Operation: `create_intelligence_schedule`

Schedule one of the 8 composites. Returns the webhook secret once.

## DELETE /v1/intelligence/schedules/{id}

Operation: `delete_intelligence_schedule`

Delete a scheduled report and its share link

## GET /v1/intelligence/schedules/{id}/report

Operation: `get_intelligence_schedule_report`

Latest report for a schedule, unredacted, for the owning organization

## POST /v1/intelligence/schedules/{id}/run

Operation: `run_intelligence_schedule`

Run a schedule now. Does not move the next cron boundary.

## GET /v1/intelligence/share/{token}

Operation: `get_intelligence_share`

Public branded report. Subscriber identifiers are redacted. ?format=html returns a page.

## GET /v1/invoices

Operation: `list_invoices`

List invoices for the caller's org, newest first

## POST /v1/invoices

Operation: `create_invoice`

Bill a period from its call records. Requires the 'write' scope.

Aggregates the org's CDR events for the period into one line per ICCID, prices each at `price_per_gb_cents` (falling back to the org rate when zero or omitted), adds the configured platform fee, and stores the result as a draft. Nothing is sent to the recipient here. The figures are frozen at creation, so running this again after late CDRs arrive produces a second invoice rather than correcting the first.

## GET /v1/invoices/{id}

Operation: `get_invoice`

One invoice with its line items and period

## PATCH /v1/invoices/{id}

Operation: `update_invoice`

Update invoice status or recipient. Requires the 'write' scope.

Omitted fields keep their current value. Moving the status to `sent` stamps `issued_at` once and later updates do not restamp it. `paid` cannot be set here, because payment is recorded from Stripe.

## DELETE /v1/invoices/{id}

Operation: `void_invoice`

Void an invoice. Requires the 'write' scope.

A void, not a delete: the row stays and its status becomes `void`, so the numbering and the audit trail survive. A paid invoice cannot be voided.

## GET /v1/invoices/{id}/pdf

Operation: `get_invoice_document`

Download the invoice document. Not JSON.

Two content types, both 200, so a client has to read the header rather than assume. A stored PDF is streamed as `application/pdf` with a download disposition. Otherwise the same invoice is rendered as `text/html` for inline display, because Workers cannot run the PDF renderer in V8. Errors are still JSON.

## GET /v1/invoices/config

Operation: `get_billing_config`

Billing config for the caller's org

Creates the row with defaults on first read, so this never 404s: USD, cycle day 1, zero rates, prefix INV, Stripe not connected.

## PUT /v1/invoices/config

Operation: `update_billing_config`

Update billing config. Requires the 'write' scope.

Every field is optional and an omitted field keeps its current value. `billing_cycle_day` and the Stripe fields cannot be set here: the first is fixed, the second belongs to the Connect onboarding routes.

## GET /v1/invoices/periods

Operation: `list_invoice_periods`

Billing periods for the caller's org, newest first

At most 24 periods. A period is the month an invoice bills for, and its id is what POST /v1/invoices takes as `period_id`.

## POST /v1/invoices/stripe/connect/begin

Operation: `stripe_connect_begin`

Start Stripe Connect onboarding (free-tier; Clerk auth only).

Returns a Stripe onboarding URL to send the operator to. The Express account is created on the first call and reused after that, so calling this again mints a fresh link for the same account rather than a second account. `account_type` is accepted but Carrier creates an Express account either way. Does not require a paid write scope or an OCS token — this is the stranger 'Start selling now' path (CAR-288). Payouts and invoice writes still require the write scope.

## GET /v1/invoices/stripe/connect/status

Operation: `stripe_connect_status`

Stripe Connect account status for the caller's org

Asks Stripe when an account exists and writes the answer back, so this is how a pending account becomes active. With no account, or when the Stripe call fails, it answers from the stored config with every capability flag false. `requirements`, `capabilities` and `payout_schedule` are always null today.

## GET /v1/location-zones

Operation: `list_location_zones`

List location zones for the reseller

## POST /v1/location-zones

Operation: `create_location_zone`

Create a new location zone

## PUT /v1/location-zones/{locationZoneId}/network-profile

Operation: `change_network_profile_of_location_zone`

Attach or change the network profile on an existing location zone. Admin scope and a confirm token, because this addresses a zone by id and other tenants may route through it.

## GET /v1/location-zones/detailed

Operation: `list_detailed_location_zones`

List location zones with full country/network detail

## GET /v1/network-default

Operation: `get_network_default`

Read the org network default (cheapest_available, all or custom) and what applies today. New assignments only.

## PUT /v1/network-default

Operation: `set_network_default`

Set the org network default. Stored only; touches no SIM and writes nothing to OCS. Applies to new assignments.

## POST /v1/network-default/plan

Operation: `plan_network_default`

Plan which networks a SIM gets in given countries: the cheapest available by default, unless the org or this call overrides it. Read only; falls back to all networks with an explicit warning when the tariff is missing.

## GET /v1/network-profiles

Operation: `list_network_profiles`

List network profiles available to the reseller

## GET /v1/onboarding

Operation: `get_onboarding_state`

Read this org's onboarding progress

Returns the same state the console onboarding UI renders, so a terminal can resume where a browser left off. Available before an OCS token is connected.

## POST /v1/onboarding/complete

Operation: `onboarding_complete`

Mark onboarding complete (step 5)

## POST /v1/onboarding/managed-account

Operation: `onboarding_start_managed_account`

Provision a managed OCS sub-account (step 2, primary path)

Re-links the org to its EXISTING sub-account under Carrier's reseller and switches the org to managed mode, removing any stored BYO token. Idempotent and read-only on OCS: it matches the org id inside an existing `listResellerAccount` account name. OCS exposes no createAccount method (G-05), so when no such account exists — or the master credential is missing — the response is still 200 with `pending: true`, a `pending_reason` of `account_absent` / `account_conflict` / `not_configured` / `ocs_unavailable`, and `managed_path_chosen` recorded so the operator can reach Stripe Connect without a paid plan (CAR-288). Never writes account_mode=managed without a live sub-account id.

## POST /v1/onboarding/ocs-connect

Operation: `onboarding_connect_ocs_token`

Connect a BYO eSIMVault token (step 2)

Verifies the token against OCS `getResellerInfo`, then stores it AES-256-GCM encrypted as `esimvault_token_enc`. The token is never returned. Connecting a token switches the org to BYO mode and clears any managed sub-account.

## POST /v1/onboarding/org

Operation: `onboarding_save_org`

Save operator details (step 1)

## POST /v1/onboarding/plan

Operation: `onboarding_save_plan`

Record the starting plan template (step 3)

## POST /v1/onboarding/plan/skip

Operation: `onboarding_skip_plan`

Skip the plan step (step 3)

## POST /v1/onboarding/test-esim

Operation: `onboarding_test_esim`

Preview one eSIM from the connected fleet (step 4)

Nothing is provisioned — this reads back the first subscriber OCS returns. Every failure degrades to a demo ICCID with a `demo_reason`, because a blocked last step is worse than an obviously fake number.

## GET /v1/operator-billing/balance

Operation: `operator_billing_get_balance`

Available and pending balance on this operator's connected Stripe account

In Stripe test mode the org sells on Carrier's shared platform test account, so no per-org balance exists: the response has stripe_mode "test", empty available/pending and a note.

## GET /v1/operator-billing/charges

Operation: `operator_billing_list_charges`

List storefront payments (charges) on this operator's connected Stripe account

## POST /v1/operator-billing/connect/begin

Operation: `operator_billing_connect_begin`

Initiate Stripe Connect onboarding (free-tier; Clerk auth only)

## POST /v1/operator-billing/connect/dashboard-link

Operation: `operator_billing_connect_dashboard_link`

Generate Stripe Express Dashboard login link

## POST /v1/operator-billing/connect/oauth/begin

Operation: `operator_billing_connect_oauth_begin`

Start Standard Connect OAuth (existing verified Stripe, no second KYC)

## POST /v1/operator-billing/connect/oauth/complete

Operation: `operator_billing_connect_oauth_complete`

Finish Standard Connect OAuth and attach stripe_user_id

## GET /v1/operator-billing/connect/status

Operation: `operator_billing_connect_status`

Get Stripe Connect account status and requirements

## GET /v1/operator-billing/disputes

Operation: `operator_billing_list_disputes`

List active and historical disputes

## POST /v1/operator-billing/disputes/{id}/evidence

Operation: `operator_billing_submit_dispute_evidence`

Submit evidence for a dispute

## GET /v1/operator-billing/fees

Operation: `operator_billing_list_fees`

List Carrier platform fees earned (admin scope required)

## GET /v1/operator-billing/fraud/reviews

Operation: `operator_billing_list_fraud_reviews`

List Radar review queue

## POST /v1/operator-billing/fraud/reviews/{id}/approve

Operation: `operator_billing_approve_fraud_review`

Approve a Radar review (admin scope required)

## POST /v1/operator-billing/fraud/reviews/{id}/decline

Operation: `operator_billing_decline_fraud_review`

Not supported: Stripe has no Radar review decline (admin scope required)

Always answers 501 and sends nothing to Stripe. Refund the charge with POST /v1/operator-billing/refunds (Carrier). If the payment was authorized but not captured, cancel its PaymentIntent in Stripe (Stripe API POST /v1/payment_intents/:id/cancel); Carrier has no route for that. Either closes the review. See https://docs.stripe.com/radar/reviews.

## GET /v1/operator-billing/fraud/value-lists

Operation: `operator_billing_list_value_lists`

List Radar block/allow value lists

## POST /v1/operator-billing/fraud/value-lists/items

Operation: `operator_billing_add_value_list_item`

Add an item to a Radar block/allow list (admin scope required)

## POST /v1/operator-billing/identity/sessions

Operation: `operator_identity_create_session`

Record a new Stripe Identity verification session

## PATCH /v1/operator-billing/identity/sessions/{id}

Operation: `operator_identity_update_session`

Update a Stripe Identity session status (internal API key only)

## GET /v1/operator-billing/identity/status

Operation: `operator_identity_status`

Get Stripe Identity KYC verification status for this operator

## GET /v1/operator-billing/payouts

Operation: `operator_billing_list_payouts`

List payout history (paginated)

## POST /v1/operator-billing/payouts/manual

Operation: `operator_billing_create_manual_payout`

Create a manual payout to the connected bank account

## GET /v1/operator-billing/payouts/schedule

Operation: `operator_billing_get_payout_schedule`

Get current payout schedule

## PATCH /v1/operator-billing/payouts/schedule

Operation: `operator_billing_update_payout_schedule`

Update payout schedule (daily / weekly / monthly)

## GET /v1/operator-billing/promotions

Operation: `operator_list_promotions`

List promotions (coupons/promo codes) for this operator

## POST /v1/operator-billing/promotions

Operation: `operator_create_promotion`

Persist a new promotion record (Stripe coupon already created by console)

## GET /v1/operator-billing/refunds

Operation: `operator_billing_list_refunds`

List refunds issued by this operator

## POST /v1/operator-billing/refunds

Operation: `operator_billing_create_refund`

Issue a refund against a Stripe charge

## POST /v1/operator-billing/stripe-webhook

Operation: `operator_billing_stripe_webhook`

Stripe Connect lifecycle webhook (no bearer auth — Stripe-Signature header required)

## GET /v1/package-templates

Operation: `list_package_templates`

List available package templates

## POST /v1/package-templates

Operation: `create_package_template`

Create a new package template

## PUT /v1/package-templates/{templateId}/core

Operation: `modify_template_core`

Modify core settings of a package template (admin only)

## PUT /v1/package-templates/{templateId}/recurring

Operation: `modify_template_recurring`

Modify recurring settings of a package template (admin only)

## PUT /v1/package-templates/{templateId}/throttling

Operation: `modify_template_throttling`

Modify throttling settings of a package template (admin only)

## GET /v1/reseller

Operation: `get_reseller_info`

Get authenticated reseller profile and balance

## GET /v1/reseller/webhook-config

Operation: `carrier_webhook_config`

Read the Bridge4IP relay flags and notification webhooks for this reseller

## POST /v1/signup

Operation: `signup_start`

Create a Carrier account and send the email verification code

Headless sign-up for the CLI. Creates a Clerk sign-up and sends a verification code to the address given. Unauthenticated by necessity — the caller has no credential yet — and rate limited by client IP and email address. Returns a short-lived client token that POST /v1/signup/verify needs.

## POST /v1/signup/verify

Operation: `signup_verify`

Submit the emailed code and create the organization

Completes the sign-up started by POST /v1/signup and creates the caller's organization. A wrong code answers 400 with Clerk's form_code_incorrect and can be retried. Sign in normally afterwards — this route returns no session credential.

## GET /v1/sponsors

Operation: `list_sponsors`

List sponsor entries for the reseller

## GET /v1/steering-lists

Operation: `list_steering_lists`

List network steering lists for the reseller

## GET /v1/storefronts

Operation: `list_storefronts`

List hosted storefronts for the caller's organization

## POST /v1/storefronts

Operation: `create_storefront`

Create a hosted storefront

## GET /v1/storefronts/{id}

Operation: `get_storefront`

Read one hosted storefront owned by the caller

## PATCH /v1/storefronts/{id}

Operation: `update_storefront`

Update a hosted storefront. Omitted fields are left as they are.

## POST /v1/storefronts/{id}/plans

Operation: `upsert_storefront_plan`

Create or replace a plan on a hosted storefront. product_id copies a connectivity plan owned by the caller.

## PATCH /v1/storefronts/{id}/plans/{slug}

Operation: `update_storefront_plan`

Change the price, sale state, display order, or stored Stripe Price of an existing plan. The Price id and its mode are set together; null clears both. The Price is stored, not yet charged. Omitted fields stay as stored. A plan on another storefront is not found.

## GET /v1/storefronts/orders

Operation: `list_storefront_orders`

List orders across the caller's storefronts, newest first. Metadata only — no ICCID, QR, or activation codes.

## GET /v1/subscribers

Operation: `list_subscribers`

List subscribers with optional filters

## POST /v1/subscribers

Operation: `create_subscriber`

Subscriber creation is not available on the OCS

Returns 501 PROVISIONING_NOT_SUPPORTED. The OCS has no createSubscriber method. Issue a free eSIM with POST /v1/subscribers/issue-from-inventory.

## GET /v1/subscribers/{iccid}

Operation: `get_subscriber`

Get subscriber details by ICCID

## GET /v1/subscribers/{iccid}/active-period

Operation: `subscriber_active_period`

Get the active period for a subscriber

## PUT /v1/subscribers/{iccid}/balance

Operation: `modify_subscriber_balance`

Add or set the prepaid balance for a subscriber

## GET /v1/subscribers/{iccid}/bitrate

Operation: `hlr_get_bitrate`

Get HLR bitrate limit for a subscriber

## PUT /v1/subscribers/{iccid}/bitrate

Operation: `hlr_set_bitrate`

Set HLR bitrate limit for a subscriber

## GET /v1/subscribers/{iccid}/cell-location

Operation: `get_subscriber_location_by_cell_id`

Resolve the subscriber's last radio cell tuple to lat/lon via Bridge4IP GeoSense. Returns 404 when that radio has no cell tower tuple.

## PUT /v1/subscribers/{iccid}/contact

Operation: `modify_subscriber_contact_info`

Update subscriber contact information

## GET /v1/subscribers/{iccid}/country-history

Operation: `subscriber_country_history`

Recent country.entered events for one subscriber

Reads the OCS event ring. limit defaults to 20 and must be 1–50.

## POST /v1/subscribers/{iccid}/gz-counter/reset

Operation: `reset_subscriber_gz_counter`

Reset the Green Zone volume counter for a subscriber (admin only, irreversible)

## GET /v1/subscribers/{iccid}/location

Operation: `get_subscriber_location`

Get last known location of a subscriber

## PUT /v1/subscribers/{iccid}/mobile-plan

Operation: `modify_subscriber_mobile_plan`

Change the mobile plan assigned to a subscriber

## POST /v1/subscribers/{iccid}/network-events

Operation: `subscriber_network_events`

Get subscriber network events over a date range

## GET /v1/subscribers/{iccid}/notes

Operation: `get_subscriber_notes`

Operator notes for one subscriber

OCS has no notes field. `notes` is the fleet drawer's single blob (empty string when unset). `data` is the support desk's append-only history, newest first, at most 100 entries. Both are scoped to the caller's org.

## POST /v1/subscribers/{iccid}/notes

Operation: `add_subscriber_note`

Append one support note

Body is { note, author? }. note is 1–4000 characters after trim. Writes an entry; it does not change the fleet blob.

## PUT /v1/subscribers/{iccid}/notes

Operation: `put_subscriber_notes`

Replace the fleet note blob

Body is { notes }. notes is a string of at most 4000 characters. Empty string clears the blob. Does not append history.

## GET /v1/subscribers/{iccid}/packages

Operation: `list_subscriber_packages`

List active data packages for a subscriber

## POST /v1/subscribers/{iccid}/packages

Operation: `assign_package`

Assign a one-time data package to a subscriber

## DELETE /v1/subscribers/{iccid}/packages

Operation: `clean_all_packages`

Remove all packages from a subscriber (admin only)

## DELETE /v1/subscribers/{iccid}/packages/{packageId}

Operation: `delete_subscriber_package`

Delete a subscriber package (admin only)

## PUT /v1/subscribers/{iccid}/packages/{packageId}/active-period

Operation: `modify_subscriber_package_active_period`

Change the active period of a prepaid package on a subscriber

## PUT /v1/subscribers/{iccid}/packages/{packageId}/expiry

Operation: `modify_package_expiry`

Modify the expiry date of an assigned package

## PUT /v1/subscribers/{iccid}/packages/{packageId}/limits

Operation: `modify_package_limits`

Modify data limits for an assigned package

## PUT /v1/subscribers/{iccid}/packages/{packageId}/recurring-status

Operation: `stop_resume_recurring_package`

Stop or resume a recurring package subscription

## PUT /v1/subscribers/{iccid}/packages/{packageId}/status

Operation: `modify_package_status`

Pause or resume an assigned package

## POST /v1/subscribers/{iccid}/packages/recurring

Operation: `assign_recurring_package`

Assign a recurring data package to a subscriber

## PUT /v1/subscribers/{iccid}/phone-number

Operation: `affect_subscriber_phone_number`

Assign a fake or real MSISDN to a subscriber

## GET /v1/subscribers/{iccid}/sim-status

Operation: `get_sim_provider_status`

Get SIM provider status for a subscriber

## PUT /v1/subscribers/{iccid}/sim-status

Operation: `change_sim_status`

Change SIM card status at the platform level (admin only)

## POST /v1/subscribers/{iccid}/sms

Operation: `send_sms`

Send an MT SMS to a subscriber (admin only)

## PUT /v1/subscribers/{iccid}/status

Operation: `modify_subscriber_status`

Activate, suspend, or deactivate a subscriber

## PUT /v1/subscribers/{iccid}/steering-list

Operation: `modify_subscriber_steering_list`

Assign a steering list to a subscriber

## POST /v1/subscribers/{iccid}/steering-list/push

Operation: `push_steering_to_subscriber`

Push the assigned steering list down to the subscriber's SIM

## PUT /v1/subscribers/{iccid}/traffic-restrictions

Operation: `set_subscriber_traffic_restrictions`

Set data/voice/SMS traffic restrictions for a subscriber

## POST /v1/subscribers/{iccid}/usage

Operation: `subscriber_usage`

Get subscriber data usage over a date range

## PUT /v1/subscribers/{iccid}/voip-plan

Operation: `modify_subscriber_voip_plan`

Change the VoIP plan assigned to a subscriber

## POST /v1/subscribers/issue-from-inventory

Operation: `issue_esim_from_inventory`

Issue a free eSIM from an account's inventory and assign a package

Durable inventory issuance requires BILLING_DB and a stable X-Idempotency-Key (Idempotency-Key is an alias). The atomically claimed key permits at most one provider dispatch, not provider exactly-once delivery. Replays recheck caller/tenant/account authorization and the account/template/window fingerprint. Preserve the key after OUTCOME_UNKNOWN or MANUAL_RECONCILIATION_REQUIRED; never automatically reissue. Managed and scoped BYO callers may select only an authorized positive account.

## DELETE /v1/subscribers/issue-from-inventory/{idempotencyKey}

Operation: `rollback_esim_issuance`

Roll back one operator issuance by its idempotency key

Releases the package OCS assigned. Current caller/account ownership is rechecked. The conditional rollback_pending claim locks the issuance before the call; only a confirmed provider release records rolled_back. An ambiguous response records rollback_unknown and must not reissue. A row with no subsPackageId cannot be rolled back automatically.

## GET /v1/tariffs/{resellerId}

Operation: `get_tariff`

Get tariff rules for a reseller

## GET /v1/voip-tariffs

Operation: `list_subscriber_voip_tariff`

List the VoIP tariff catalog available to a reseller

## GET /v1/voip-tariffs/{voipPlanId}/rules

Operation: `list_voip_tariff_rule`

List the rate rules for one VoIP plan

## POST /v1/webhooks/bridge4ip

Operation: `receive_bridge4ip_webhook`

Receive Bridge4IP push event (usage, eSIM status, location)
