Docs
Home
  • Overview
  • Web installer
  • Production deployment
  • aaPanel
  • aaPanel quick reference
  • Storefront & customers
  • Admin
  • eSIM providers
  • AI assistant
  • Flutter app
  • Mobile API
Home
  • Overview
  • Web installer
  • Production deployment
  • aaPanel
  • aaPanel quick reference
  • Storefront & customers
  • Admin
  • eSIM providers
  • AI assistant
  • Flutter app
  • Mobile API
  • Setup & deployment

    • Setup & deployment
    • Web installer
    • Production deployment overview
    • Deployment on aaPanel
    • aaPanel quick reference (esimScan)
  • Guides

    • Storefront & customers
    • Admin
    • eSIM provider configuration
    • AI assistant
  • Mobile app

    • Mobile app (Flutter)
    • Mobile API (/api/v1)

Mobile API (/api/v1)

esimScan ships a REST API for mobile clients, mounted at /api/v1. It powers the Flutter mobile app, and you can build any other client against it — a second app, a kiosk, a partner integration.

It is enabled by default and needs no configuration beyond running migrations.

Authentication

Bearer tokens issued by Laravel Sanctum. The token belongs to a CustomerAccount — the same storefront buyer account used at /login, so a customer has one identity across web and mobile.

Create the token table once:

php artisan migrate      # adds personal_access_tokens

Get a token, then send it on every protected call:

curl -X POST https://yourstore.com/api/v1/auth/login \
  -H 'Accept: application/json' -H 'Content-Type: application/json' \
  -d '{"email":"buyer@example.com","password":"secret","device_name":"Pixel 8"}'

curl https://yourstore.com/api/v1/me \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer 3|xxxxxxxxxxxxxxxxxxxx'

Always send Accept: application/json — without it Laravel may answer validation errors with an HTML redirect instead of JSON.

Rate limits

ScopeLimit
Everything under /api/v190 requests/minute, per token (falls back to per IP)
POST /ai-advisor15/minute — each call hits a paid LLM API
POST /auth/register, POST /auth/login10/minute
POST /auth/forgot-password5/minute

Exceeding a limit returns 429 Too Many Requests.

Endpoints

Public

MethodPathPurpose
GET/healthLiveness probe — {"ok":true,…}
GET/configBootstrap config: store name, currency, terms/privacy URLs, feature flags, min_supported_version
GET/destinationsCatalog: every sellable country/region with a "from" price and flag
GET/destinations/{slug}One destination with all its plans, prices and coverage notes
GET/popular-packagesHighlighted best-sellers
GET/all-in-one-plansData + Voice + SMS bundles
POST/ai-advisorTrip description → plan recommendations from your catalog

POST /ai-advisor takes {"need": "10 days in Japan, maps and photos"} (3–600 characters) and returns {"ok":true,"summary":…,"recommendations":[…]}. It fails with 422 and a readable message when no AI provider is configured or the store runs in demo mode — see the AI assistant guide.

Auth

MethodPathBody
POST/auth/registername, email, password, password_confirmation
POST/auth/loginemail, password, optional device_name
POST/auth/forgot-passwordemail — always returns a generic OK, so it can't be used to probe which emails have accounts
POST/auth/logout(authenticated) revokes the current token

register and login both return {"token":…,"token_type":"Bearer","user":{…}}.

Profile

MethodPathBody
GET/me—
PUT/mename, email, optional avatar (image, ≤2 MB)
PUT/me/passwordcurrent_password, password, password_confirmation
DELETE/mepassword — permanently deletes the account

DELETE /me exists because both the App Store and Google Play require in-app account deletion. It is irreversible.

Orders and eSIMs

MethodPathReturns
GET/ordersPaginated order history
GET/esimsPurchased eSIMs — destination, data, validity, status
GET/esims/{id}One eSIM plus its top-ups, with ICCID, SM-DP+ address, activation code and a signed QR URL

The QR image is served from a signed, expiring URL (/esim/qr/{order}), the same mechanism the delivery email uses. qr_url is null until the eSIM is provisioned, so clients should show a "preparing your eSIM" state.

Checkout

MethodPathBody
POST/checkoutplan (e.g. cat-42), optional destination slug

Returns a checkout_url plus an order summary. The client opens that URL in a system browser — there is no native in-app purchase. It reuses the storefront's Stripe/PayPal flow and provisioning pipeline, and the resulting order links back to the buyer by email so it appears under /orders and /esims. See Payments for why external payment is permitted for eSIM connectivity.

Support

MethodPathBody
GET/support/tickets—
POST/support/ticketstitle, description (≤5000), optional priority (low/medium/high/urgent)
GET/support/tickets/{id}—
POST/support/tickets/{id}/replymessage (≤5000)

These are the same tickets your staff answer in Admin → Support, including the AI "suggest reply" helper.

Errors

StatusMeaning
401Missing, malformed or revoked token
403Authenticated but not allowed (e.g. a blocked customer)
404Not found, or the record belongs to another customer
422Validation failed — {"message":…,"errors":{"field":["…"]}}
429Rate limit exceeded
503A dependency (AI provider, eSIM provider) is temporarily unavailable

Versioning

The prefix is set once in bootstrap/app.php (apiPrefix: 'api/v1'), and routes live in routes/api.php. Keep /api/v1 stable for released apps — installed clients cannot be updated on your schedule. Add /api/v2 alongside it for breaking changes.

GET /config returns min_supported_version, which lets you force an upgrade prompt in older clients rather than breaking them silently.

Last Updated: 8/13/26, 7:27 PM
Prev
Mobile app (Flutter)