{"id":49891033,"url":"https://github.com/lambdachad/esim_access","last_synced_at":"2026-05-15T21:13:05.999Z","repository":{"id":353941703,"uuid":"1221573786","full_name":"lambdachad/esim_access","owner":"lambdachad","description":"eSIM Access API Client for Elixir","archived":false,"fork":false,"pushed_at":"2026-04-26T12:02:53.000Z","size":40,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-04-27T03:28:44.784Z","etag":null,"topics":["elixir","esim-access"],"latest_commit_sha":null,"homepage":"http://hexdocs.pm/esim_access","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lambdachad.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-26T11:57:57.000Z","updated_at":"2026-04-26T12:05:34.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/lambdachad/esim_access","commit_stats":null,"previous_names":["lambdachad/esimaccess"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/lambdachad/esim_access","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lambdachad%2Fesim_access","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lambdachad%2Fesim_access/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lambdachad%2Fesim_access/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lambdachad%2Fesim_access/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lambdachad","download_url":"https://codeload.github.com/lambdachad/esim_access/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lambdachad%2Fesim_access/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33080698,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-15T20:25:35.270Z","status":"ssl_error","status_checked_at":"2026-05-15T20:25:34.732Z","response_time":103,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["elixir","esim-access"],"created_at":"2026-05-15T21:13:05.152Z","updated_at":"2026-05-15T21:13:05.985Z","avatar_url":"https://github.com/lambdachad.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# EsimAccess\n\nElixir client for the [eSIM Access](https://esimaccess.com) wholesale eSIM reseller API.\n\nOrder, query, top up, suspend, and cancel eSIM profiles across 185+ countries.\nIncludes typed structs for all responses, a webhook handler with Plug integration\nfor Phoenix, and comprehensive test coverage.\n\n## Installation\n\nAdd `esim_access` to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:esim_access, \"~\u003e 0.1.0\"}\n  ]\nend\n```\n\n## Quick Start\n\n```elixir\n# 1. Create a config with your access code\nconfig = EsimAccess.new(access_code: \"your_access_code\")\n\n# 2. Check your balance\n{:ok, %{balance: balance}} = EsimAccess.Balance.query(config)\n# balance is value * 10,000 -- so 100_000 = $10.00\n\n# 3. Browse available packages\n{:ok, packages} = EsimAccess.Packages.list(config, %{location_code: \"JP\"})\n\n# 4. Order an eSIM\n{:ok, order} = EsimAccess.Orders.create(config, %{\n  transaction_id: \"txn_#{System.os_time(:millisecond)}\",\n  package_info_list: [\n    %{package_code: \"JP_1_7\", count: 1}\n  ]\n})\n\n# 5. Query the allocated profile (may take up to 30s)\n{:ok, {profiles, _pager}} = EsimAccess.Profiles.query(config, %{\n  order_no: order.order_no,\n  pager: %{page_num: 1, page_size: 50}\n})\n\nprofile = hd(profiles)\n# profile.ac        -\u003e LPA activation code\n# profile.qr_code_url -\u003e QR code image URL\n# profile.iccid     -\u003e eSIM ICCID\n```\n\n## Configuration\n\nCreate a config struct with `EsimAccess.new/1`. The struct is passed as the\nfirst argument to every API call -- no global state, no application config.\n\n```elixir\n# Production\nconfig = EsimAccess.new(access_code: \"your_access_code\")\n\n# Custom base URL (for testing/proxying)\nconfig = EsimAccess.new(\n  access_code: \"your_access_code\",\n  base_url: \"https://your-proxy.example.com\"\n)\n```\n\nGet your access code from the\n[eSIM Access developer console](https://console.esimaccess.com/developer/index).\n\n## API Reference\n\nAll functions return `{:ok, result}` or `{:error, %EsimAccess.Error{}}`.\n\n### Packages\n\n```elixir\n# All packages\n{:ok, packages} = EsimAccess.Packages.list(config)\n\n# Filter by country (Alpha-2 ISO code)\n{:ok, packages} = EsimAccess.Packages.list(config, %{location_code: \"US\"})\n\n# Regional or global packages\n{:ok, packages} = EsimAccess.Packages.list(config, %{location_code: \"!RG\"})\n{:ok, packages} = EsimAccess.Packages.list(config, %{location_code: \"!GL\"})\n\n# Day Pass plans only\n{:ok, packages} = EsimAccess.Packages.list(config, %{data_type: \"2\"})\n\n# Top-up packages for a specific ICCID\n{:ok, packages} = EsimAccess.Packages.list(config, %{\n  type: \"TOPUP\",\n  iccid: \"89852...\"\n})\n```\n\n### Orders\n\n```elixir\n# Single order\n{:ok, order} = EsimAccess.Orders.create(config, %{\n  transaction_id: \"unique_txn_id\",\n  package_info_list: [%{package_code: \"JP_1_7\", count: 1}]\n})\n\n# With price verification (fails if price changed)\n{:ok, order} = EsimAccess.Orders.create(config, %{\n  transaction_id: \"unique_txn_id\",\n  amount: 15000,\n  package_info_list: [\n    %{package_code: \"JP_1_7\", count: 1, price: 15000}\n  ]\n})\n\n# Daily plan with specific number of days\n{:ok, order} = EsimAccess.Orders.create(config, %{\n  transaction_id: \"unique_txn_id\",\n  package_info_list: [\n    %{package_code: \"SG_1_Daily\", count: 1, period_num: 5}\n  ]\n})\n```\n\n### Profiles\n\n```elixir\n# Query by order number\n{:ok, {profiles, pager}} = EsimAccess.Profiles.query(config, %{\n  order_no: \"B23051616050537\",\n  pager: %{page_num: 1, page_size: 50}\n})\n\n# Query by ICCID\n{:ok, {profiles, pager}} = EsimAccess.Profiles.query(config, %{\n  iccid: \"89852246280001113119\",\n  pager: %{page_num: 1, page_size: 50}\n})\n\n# Cancel unused profile (refundable)\n{:ok, _} = EsimAccess.Profiles.cancel(config, %{esim_tran_no: \"23120118156818\"})\n\n# Suspend / unsuspend data service\n{:ok, _} = EsimAccess.Profiles.suspend(config, %{esim_tran_no: \"23120118156818\"})\n{:ok, _} = EsimAccess.Profiles.unsuspend(config, %{esim_tran_no: \"23120118156818\"})\n\n# Revoke profile (non-refundable)\n{:ok, _} = EsimAccess.Profiles.revoke(config, %{esim_tran_no: \"23120118156818\"})\n```\n\n### Top Up\n\n```elixir\n{:ok, result} = EsimAccess.TopUp.create(config, %{\n  esim_tran_no: \"23072017992029\",\n  package_code: \"TOPUP_JC172\",\n  transaction_id: \"topup_txn_001\"\n})\n# result.expired_time  -\u003e new expiry\n# result.total_volume  -\u003e new total data (bytes)\n```\n\n### Balance\n\n```elixir\n{:ok, %{balance: balance}} = EsimAccess.Balance.query(config)\n# balance is value * 10,000 (100_000 = $10.00)\n```\n\n### Usage\n\n```elixir\n# Check usage for up to 10 eSIMs (updated every 2-3 hours)\n{:ok, usages} = EsimAccess.Usage.check(config, [\"25030303480009\"])\n# usages -\u003e [%{esim_tran_no, data_usage, total_data, last_update_time}]\n```\n\n### SMS\n\n```elixir\n{:ok, _} = EsimAccess.Sms.send(config, %{\n  esim_tran_no: \"23072017992029\",\n  message: \"Your verification code is 123456\"\n})\n```\n\n### Locations\n\n```elixir\n{:ok, locations} = EsimAccess.Locations.list(config)\n# type 1 = single country, type 2 = multi-country region\n```\n\n### Webhooks\n\n```elixir\n# Set webhook URL\n{:ok, _} = EsimAccess.Webhook.save(config, \"https://your-app.com/webhooks/esim\")\n\n# Query current webhook\n{:ok, %{\"webhook\" =\u003e url}} = EsimAccess.Webhook.query(config)\n```\n\n## Webhook Handling\n\nThe library provides typed event structs, a handler behaviour, and a Plug for\nreceiving eSIM Access webhook notifications in Phoenix.\n\n### Six Event Types\n\n| Event | Description |\n|-------|-------------|\n| `OrderStatus` | Order fulfilled -- profiles ready for download |\n| `EsimStatus` | eSIM lifecycle changes (in use, expired, etc.) |\n| `SmdpEvent` | Low-level SM-DP+ profile state transitions |\n| `DataUsage` | Data consumption threshold alerts (50%, 90%) |\n| `ValidityUsage` | Validity period expiry warnings (1 day left) |\n| `CheckHealth` | Connectivity check on initial webhook setup |\n\n### 1. Define a Handler\n\n```elixir\ndefmodule MyApp.EsimWebhookHandler do\n  @behaviour EsimAccess.Webhooks.Handler\n\n  alias EsimAccess.Webhooks.Event\n\n  @impl true\n  def handle_event(%Event.OrderStatus{order_status: \"GOT_RESOURCE\"} = event) do\n    # Profiles allocated -- fetch ICCID and QR code\n    MyApp.Esim.fetch_profiles(event.order_no, event.transaction_id)\n    :ok\n  end\n\n  @impl true\n  def handle_event(%Event.DataUsage{remain_threshold: threshold} = event) do\n    if threshold \u003c= 0.1 do\n      MyApp.Notifications.send_low_data_warning(event.transaction_id)\n    end\n    :ok\n  end\n\n  @impl true\n  def handle_event(%Event.ValidityUsage{remain: 1} = event) do\n    MyApp.Notifications.send_expiry_warning(event.transaction_id)\n    :ok\n  end\n\n  @impl true\n  def handle_event(_event), do: :ok\nend\n```\n\n### 2. Add the Plug to Your Router\n\n```elixir\n# In your Phoenix router\nforward \"/webhooks/esim\", EsimAccess.Webhooks.Plug,\n  handler: MyApp.EsimWebhookHandler\n```\n\nOr with IP verification:\n\n```elixir\nforward \"/webhooks/esim\", EsimAccess.Webhooks.Plug,\n  handler: MyApp.EsimWebhookHandler,\n  verify_ip: true\n```\n\n### 3. Or Parse Events Manually in a Controller\n\n```elixir\ndefmodule MyAppWeb.EsimWebhookController do\n  use MyAppWeb, :controller\n\n  def handle(conn, params) do\n    case EsimAccess.Webhooks.Event.parse(params) do\n      {:ok, event} -\u003e\n        MyApp.EsimWebhookHandler.handle_event(event)\n        send_resp(conn, 200, \"ok\")\n\n      {:error, _reason} -\u003e\n        send_resp(conn, 400, \"invalid event\")\n    end\n  end\nend\n```\n\n## Conventions\n\n### Prices\n\nAll prices are expressed as **value * 10,000**. For example, `10000` = $1.00 USD.\n\n### Data Volumes\n\nAll data volumes are in **bytes**. Common conversions:\n\n| Value | Bytes |\n|-------|-------|\n| 100 MB | 104,857,600 |\n| 1 GB | 1,073,741,824 |\n| 5 GB | 5,368,709,120 |\n\n### Identifiers\n\nMost profile operations accept either `iccid` or `esim_tran_no`. Prefer\n`esim_tran_no` because ICCIDs can be reused across profiles.\n\n### Rate Limits\n\nThe API enforces a limit of **8 requests per second**. The client includes\nautomatic retry with backoff for transient errors.\n\n## Error Handling\n\nAll API functions return `{:error, %EsimAccess.Error{}}` on failure:\n\n```elixir\ncase EsimAccess.Orders.create(config, params) do\n  {:ok, order} -\u003e\n    # success\n\n  {:error, %EsimAccess.Error{error_code: \"200007\"}} -\u003e\n    # insufficient balance\n\n  {:error, %EsimAccess.Error{error_code: code, error_message: msg}} -\u003e\n    Logger.error(\"eSIM API error [#{code}]: #{msg}\")\nend\n```\n\nSee `EsimAccess.Error` module docs for the full error code reference.\n\n## Testing\n\n```bash\n# Unit tests (no API key needed)\nmix test\n\n# Integration tests (requires access code)\nESIM_ACCESS_CODE=your_code mix test --include integration\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flambdachad%2Fesim_access","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flambdachad%2Fesim_access","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flambdachad%2Fesim_access/lists"}