Skip to content
Documentation
EN
English简体中文繁體中文
Workspace
GuidesAPI referenceAPI v1
OpenAPI reference · EnglishDownload OpenAPI ↓

Requests use your API key and the selected server. Purchase and renewal requests can spend your wallet balance.

v1.1.0
OpenAPI 3.1.0

Customer API

Manage proxies, traffic and billing from your own application.

Send your workspace API key as a Bearer token. Each endpoint lists its required permissions. Confirming a purchase or renewal charges your wallet.

Start with a guide · Understand retries

Server:https://portal.migoip.com

Workspace API

Client Libraries

Account

​

Wallet balance for your workspace

​

Required permissions: wallet:read.

Returns the same wallet DTO the portal uses.

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/balance
curl https://portal.migoip.com/api/customer/v1/balance \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "tenant_id": "string",
  "balance_fen": "string"
}

Identify the authenticated workspace

​

Required permissions: account:read.

Requires account:read. Returns the workspace and issuer derived from the API key, its base permission and explicit scopes (null for a legacy key). No customer-selected identity parameters or secrets.

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/account
curl https://portal.migoip.com/api/customer/v1/account \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "tenant_id": "string",
  "user_id": "string",
  "permission": "read",
  "scopes": [
    "account:read"
  ]
}

Static pricing

​

Available prices for your workspace

​

Required permissions: catalog:read.

Exactly the tenant-effective type -> country -> location price resolution the portal uses, with exact terms and prices. Internal pricing provenance is not exposed.

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/catalog
curl https://portal.migoip.com/api/customer/v1/catalog \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "items": [
    {
      "location_kind": "city",
      "id": "string",
      "product": "static_residential",
      "country": "string",
      "location_id": "string",
      "location_name": "string",
      "location_subdivision": "string",
      "period": "string",
      "days": 1,
      "base_fen": "string",
      "optimization_fen": "string",
      "optimization_enabled": true,
      "optimization_supported": true,
      "supported_protocols": [],
      "supply_mode": "string"
    }
  ],
  "total": 0
}

Create a purchase quote

​

Required permissions: orders:write.

Write permission required. Freezes the effective unit and total price for the requested quantity and protocol. The response never contains the simulator scenario or any supplier field. Unknown body fields (including tenant_id and scenario) are rejected with 422. The price_id must identify a named-location offer. Country/product rules and country-only synthesized IDs return 422 LOCATION_REQUIRED.

Body·

required
application/json

Unknown fields, including tenant_id and scenario, are rejected with 422.

  • Integer numbers.

  • Select the independently priced per-IP access-optimization add-on; rejected when the effective offer does not support it

  • Opt the purchase into wallet auto-renew; non-financial until the scheduler charges

  • Optional one-per-purchase percentage coupon code. Trimmed and upper-cased server-side; an unknown

  • Optional plain-text instance note of at most 200 Unicode characters; a multi-IP purchase suffixes each instance by its durable order-item ordinal

  • A connection-format preference

Responses

  • application/json
  • application/json
Request Example for post/api/customer/v1/quotes
curl https://portal.migoip.com/api/customer/v1/quotes \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN' \
  --data '{
  "price_id": "",
  "quantity": 1,
  "protocol": "http",
  "access_optimized": false,
  "note": "",
  "auto_renew_enabled": false,
  "coupon_code": ""
}'
{
  "purchase_kind": "purchase",
  "service_id": "string",
  "service_expires_at": "2026-10-26T05:45:59.177Z",
  "id": "string",
  "tenant_id": "string",
  "price_id": "string",
  "product": "static_residential",
  "country": "string",
  "location_id": "string",
  "location_name": "string",
  "location_subdivision": "string",
  "period": "string",
  "days": 1,
  "quantity": 1,
  "protocol": "http",
  "access_optimized": true,
  "optimization_fen": "string",
  "unit_fen": "string",
  "subtotal_fen": "string",
  "discount_fen": "string",
  "total_fen": "string",
  "coupon": {
    "id": "string",
    "code": "string",
    "name": "string",
    "discount_bps": 1
  },
  "created_at": "2026-10-26T05:45:59.177Z",
  "expires_at": "2026-10-26T05:45:59.177Z",
  "note": "string",
  "auto_renew_enabled": true
}

Orders

​

Confirm a quote and place the order

​

Required permissions: orders:write, wallet:spend.

Write permission required. Charges the wallet and queues exactly one fulfillment operation, in one transaction. Requires Idempotency-Key; an exact replay returns the original order and operation without another debit, and a concurrent duplicate is collapsed to the same order. Quotes without a named location return 422 LOCATION_REQUIRED before debit.

Headers

  • Exact replay returns the original result without spending twice; a changed body conflicts.

Body

required
application/json

Responses

  • application/json
  • application/json
Request Example for post/api/customer/v1/orders
curl https://portal.migoip.com/api/customer/v1/orders \
  --request POST \
  --header 'Idempotency-Key: ' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN' \
  --data '{
  "quote_id": ""
}'
{
  "order": {
    "purchase_kind": "purchase",
    "service_id": "string",
    "id": "string",
    "tenant_id": "string",
    "quote_id": "string",
    "subtotal_fen": "string",
    "discount_fen": "string",
    "total_fen": "string",
    "coupon": {
      "id": "string",
      "code": "string",
      "name": "string",
      "discount_bps": 1
    },
    "status": "provisioning",
    "created_at": "2026-10-26T05:45:59.177Z"
  },
  "operations": [
    {
      "id": "string",
      "order_id": "string",
      "kind": "string",
      "state": "queued",
      "error_code": "string",
      "attempt_count": 0,
      "created_at": "2026-10-26T05:45:59.177Z",
      "updated_at": "2026-10-26T05:45:59.177Z"
    }
  ]
}

List your workspace’s orders

​

Required permissions: orders:read.

Newest first, bounded pagination.

Query Parameters

  • Integer numbers.

  • Integer numbers.

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/orders
curl 'https://portal.migoip.com/api/customer/v1/orders?page=1&page_size=20' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "items": [
    {
      "purchase_kind": "purchase",
      "service_id": "string",
      "id": "string",
      "tenant_id": "string",
      "quote_id": "string",
      "subtotal_fen": "string",
      "discount_fen": "string",
      "total_fen": "string",
      "coupon": {
        "id": "string",
        "code": "string",
        "name": "string",
        "discount_bps": 1
      },
      "status": "provisioning",
      "created_at": "2026-10-26T05:45:59.177Z"
    }
  ],
  "total": 0,
  "page": 1,
  "page_size": 1
}

Read one order and its progress

​

Required permissions: orders:read.

A foreign or unknown order id is 404; no other tenant's record is visible.

Path Parameters

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/orders/{id}
curl 'https://portal.migoip.com/api/customer/v1/orders/{id}' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "order": {
    "purchase_kind": "purchase",
    "service_id": "string",
    "id": "string",
    "tenant_id": "string",
    "quote_id": "string",
    "subtotal_fen": "string",
    "discount_fen": "string",
    "total_fen": "string",
    "coupon": {
      "id": "string",
      "code": "string",
      "name": "string",
      "discount_bps": 1
    },
    "status": "provisioning",
    "created_at": "2026-10-26T05:45:59.177Z"
  },
  "quote": {
    "purchase_kind": "purchase",
    "service_id": "string",
    "service_expires_at": "2026-10-26T05:45:59.177Z",
    "id": "string",
    "tenant_id": "string",
    "price_id": "string",
    "product": "static_residential",
    "country": "string",
    "location_id": "string",
    "location_name": "string",
    "location_subdivision": "string",
    "period": "string",
    "days": 1,
    "quantity": 1,
    "protocol": "http",
    "access_optimized": true,
    "optimization_fen": "string",
    "unit_fen": "string",
    "subtotal_fen": "string",
    "discount_fen": "string",
    "total_fen": "string",
    "coupon": {
      "id": "string",
      "code": "string",
      "name": "string",
      "discount_bps": 1
    },
    "created_at": "2026-10-26T05:45:59.177Z",
    "expires_at": "2026-10-26T05:45:59.177Z",
    "note": "string",
    "auto_renew_enabled": true
  },
  "items": [
    {
      "id": "string",
      "ordinal": 1,
      "amount_fen": "string"
    }
  ],
  "operations": [
    {
      "id": "string",
      "order_id": "string",
      "kind": "string",
      "state": "queued",
      "error_code": "string",
      "attempt_count": 0,
      "created_at": "2026-10-26T05:45:59.177Z",
      "updated_at": "2026-10-26T05:45:59.177Z"
    }
  ],
  "services": [
    {
      "id": "string",
      "order_id": "string",
      "operation_id": "string",
      "ip": "string",
      "hostname": "string",
      "product": "static_residential",
      "country": "string",
      "location_id": "string",
      "location_name": "string",
      "location_subdivision": "string",
      "refunded_at": "2026-10-26T05:45:59.177Z",
      "protocol": "http",
      "activated_at": "2026-10-26T05:45:59.177Z",
      "expires_at": "2026-10-26T05:45:59.177Z",
      "term_state": "active",
      "connection_available": true,
      "direct_connection_available": true,
      "optimized_connection_available": true,
      "supported_protocols": [],
      "renewable": true,
      "simulated": true,
      "provenance": "provider",
      "optimization": {
        "selected": true,
        "state": "pending",
        "bandwidth": {
          "upload": {
            "bytes_per_second": "string",
            "burst_bytes": "string"
          },
          "download": {
            "bytes_per_second": "string",
            "burst_bytes": "string"
          }
        }
      },
      "lifecycle": {
        "supported": true,
        "renewal_supported": true,
        "revoke_supported": true,
        "access_state": "string",
        "expires_at": "2026-10-26T05:45:59.177Z",
        "can_renew": true,
        "renewal_block_reason": "string",
        "latest_renewal": {
          "id": "string",
          "service_id": "string",
          "quote_id": "string",
          "branch": "extend",
          "amount_fen": "string",
          "state": "queued",
          "error_code": "string",
          "term_start": null,
          "expires_at": null,
          "created_at": "2026-10-26T05:45:59.177Z",
          "updated_at": "2026-10-26T05:45:59.177Z"
        },
        "prices": [
          {
            "id": "string",
            "product": "static_residential",
            "country": "string",
            "location_id": "string",
            "location_name": "string",
            "location_subdivision": "string",
            "period": "string",
            "days": 1,
            "base_fen": "string"
          }
        ]
      },
      "note": "string",
      "settings_version": 1,
      "auto_renew_enabled": true,
      "auto_renew_period": "string",
      "auto_renew_state": "off",
      "auto_renew_next_attempt_at": "2026-10-26T05:45:59.177Z"
    }
  ]
}

Poll one fulfillment operation

​

Required permissions: orders:read.

Bounded status only: id, kind, state, attempt count and timestamps. Supplier identifiers, leases, modes and raw error codes are never exposed; any internal failure is reported as OPERATION_NEEDS_ATTENTION.

Path Parameters

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/operations/{id}
curl 'https://portal.migoip.com/api/customer/v1/operations/{id}' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "id": "string",
  "order_id": "string",
  "kind": "string",
  "state": "queued",
  "error_code": "string",
  "attempt_count": 0,
  "created_at": "2026-10-26T05:45:59.177Z",
  "updated_at": "2026-10-26T05:45:59.177Z"
}

List unified order history

​

Required permissions: orders:read.

Requires orders:read. Includes static orders, optimization upgrades, renewals and dynamic traffic purchases.

Query Parameters

  • Integer numbers.

  • Integer numbers.

  • Inclusive RFC3339 or YYYY-MM-DD UTC lower bound.

  • Exclusive RFC3339 or YYYY-MM-DD UTC upper bound. Must be later than from.

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/order-history
curl 'https://portal.migoip.com/api/customer/v1/order-history?page=1&page_size=20' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "items": [
    {
      "id": "string",
      "tenant_id": "string",
      "kind": "static",
      "purchase_kind": "purchase",
      "service_id": "string",
      "product": "static_residential",
      "country": "string",
      "location_name": "string",
      "period": "string",
      "days": 0,
      "quantity": 0,
      "traffic_bytes": "string",
      "total_fen": "string",
      "status": "provisioning",
      "created_at": "2026-10-26T05:45:59.177Z"
    }
  ],
  "total": 0,
  "page": 1,
  "page_size": 1
}

Static proxies

​

List static proxies

​

Required permissions: proxies:read.

Bounded pagination; optional product and term-state filters, a literal free-text search (which may also match notes) and an exact IP filter. Each row carries the embedded lifecycle guard and its instance settings (note, optimistic version and auto-renew state), which a legacy row without settings reads as empty note, version 1 and auto-renew off.

Query Parameters

  • Integer numbers.

  • Integer numbers.

  • Recorded dates only; not a live cutoff claim

  • Literal case-insensitive free-text search; may also match instance notes

  • Comma-separated normalized exact IPv4/IPv6 addresses

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/proxies
curl 'https://portal.migoip.com/api/customer/v1/proxies?page=1&page_size=20' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "items": [
    {
      "id": "string",
      "order_id": "string",
      "operation_id": "string",
      "ip": "string",
      "hostname": "string",
      "product": "static_residential",
      "country": "string",
      "location_id": "string",
      "location_name": "string",
      "location_subdivision": "string",
      "refunded_at": "2026-10-26T05:45:59.177Z",
      "protocol": "http",
      "activated_at": "2026-10-26T05:45:59.177Z",
      "expires_at": "2026-10-26T05:45:59.177Z",
      "term_state": "active",
      "connection_available": true,
      "direct_connection_available": true,
      "optimized_connection_available": true,
      "supported_protocols": [],
      "renewable": true,
      "simulated": true,
      "provenance": "provider",
      "optimization": {
        "selected": true,
        "state": "pending",
        "bandwidth": {
          "upload": {
            "bytes_per_second": "string",
            "burst_bytes": "string"
          },
          "download": {
            "bytes_per_second": "string",
            "burst_bytes": "string"
          }
        }
      },
      "lifecycle": {
        "supported": true,
        "renewal_supported": true,
        "revoke_supported": true,
        "access_state": "string",
        "expires_at": "2026-10-26T05:45:59.177Z",
        "can_renew": true,
        "renewal_block_reason": "string",
        "latest_renewal": {
          "id": "string",
          "service_id": "string",
          "quote_id": "string",
          "branch": "extend",
          "amount_fen": "string",
          "state": "queued",
          "error_code": "string",
          "term_start": null,
          "expires_at": null,
          "created_at": "2026-10-26T05:45:59.177Z",
          "updated_at": "2026-10-26T05:45:59.177Z"
        },
        "prices": [
          {
            "id": "string",
            "product": "static_residential",
            "country": "string",
            "location_id": "string",
            "location_name": "string",
            "location_subdivision": "string",
            "period": "string",
            "days": 1,
            "base_fen": "string"
          }
        ]
      },
      "note": "string",
      "settings_version": 1,
      "auto_renew_enabled": true,
      "auto_renew_period": "string",
      "auto_renew_state": "off",
      "auto_renew_next_attempt_at": "2026-10-26T05:45:59.177Z"
    }
  ],
  "total": 0,
  "page": 1,
  "page_size": 1
}

Export currently usable plaintext connections

​

Required permissions: proxies:read, credentials:read.

Returns text/plain: one LF-terminated host:port:username:password tuple per line for a single explicit protocol, at most page_size lines. An IPv6 host is bracketed. Only connections that are usable right now are exported: an expired or otherwise credential-free instance contributes no line, so an empty export is a 200 with an empty body, never JSON. A genuine read failure fails the whole request instead of silently dropping an instance. Pagination metadata rides on the response headers, not the body. Never log the returned values. access_mode defaults to preferred. Explicit direct or optimized selection never falls back to the other route. The exact ips filter intersects product, term and search filters.

Query Parameters

  • Explicit route selection; unavailable routes produce no line

  • Comma-separated exact IPv4/IPv6 values; at most 100 normalized unique addresses. Empty/invalid tokens and repeated ips parameters are rejected.

  • HTTP maps to the stored HTTPS/CONNECT representation

  • Integer numbers.

  • Integer numbers.

Responses

  • text/plain
  • application/json
Request Example for get/api/customer/v1/proxies/export
curl 'https://portal.migoip.com/api/customer/v1/proxies/export?access_mode=preferred&protocol=http&page=1&page_size=20' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
203.0.113.10:8443:acme-user:secret

Read one instance with its permitted connection credentials

​

Required permissions: proxies:read, credentials:read.

Returns the instance plus the per-protocol connection tuples the backend permits. An expired instance exposes no usable connection. A foreign or unknown id is 404.

Path Parameters

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/proxies/{id}
curl 'https://portal.migoip.com/api/customer/v1/proxies/{id}' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "service": {
    "id": "string",
    "order_id": "string",
    "operation_id": "string",
    "ip": "string",
    "hostname": "string",
    "product": "static_residential",
    "country": "string",
    "location_id": "string",
    "location_name": "string",
    "location_subdivision": "string",
    "refunded_at": "2026-10-26T05:45:59.177Z",
    "protocol": "http",
    "activated_at": "2026-10-26T05:45:59.177Z",
    "expires_at": "2026-10-26T05:45:59.177Z",
    "term_state": "active",
    "connection_available": true,
    "direct_connection_available": true,
    "optimized_connection_available": true,
    "supported_protocols": [],
    "renewable": true,
    "simulated": true,
    "provenance": "provider",
    "optimization": {
      "selected": true,
      "state": "pending",
      "bandwidth": {
        "upload": {
          "bytes_per_second": "string",
          "burst_bytes": "string"
        },
        "download": {
          "bytes_per_second": "string",
          "burst_bytes": "string"
        }
      }
    },
    "lifecycle": {
      "supported": true,
      "renewal_supported": true,
      "revoke_supported": true,
      "access_state": "string",
      "expires_at": "2026-10-26T05:45:59.177Z",
      "can_renew": true,
      "renewal_block_reason": "string",
      "latest_renewal": {
        "id": "string",
        "service_id": "string",
        "quote_id": "string",
        "branch": "extend",
        "amount_fen": "string",
        "state": "queued",
        "error_code": "string",
        "term_start": null,
        "expires_at": null,
        "created_at": "2026-10-26T05:45:59.177Z",
        "updated_at": "2026-10-26T05:45:59.177Z"
      },
      "prices": [
        {
          "id": "string",
          "product": "static_residential",
          "country": "string",
          "location_id": "string",
          "location_name": "string",
          "location_subdivision": "string",
          "period": "string",
          "days": 1,
          "base_fen": "string"
        }
      ]
    },
    "note": "string",
    "settings_version": 1,
    "auto_renew_enabled": true,
    "auto_renew_period": "string",
    "auto_renew_state": "off",
    "auto_renew_next_attempt_at": "2026-10-26T05:45:59.177Z"
  },
  "connections": [
    {
      "protocol": "http",
      "host": "string",
      "port": 1,
      "username": "string",
      "password": "string",
      "simulated": true
    }
  ],
  "direct_connections": [
    {
      "protocol": "http",
      "host": "string",
      "port": 1,
      "username": "string",
      "password": "string",
      "simulated": true
    }
  ],
  "optimized_connections": [
    {
      "protocol": "http",
      "host": "string",
      "port": 1,
      "username": "string",
      "password": "string",
      "simulated": true
    }
  ]
}

Update one instance's non-financial settings

​

Required permissions: proxies:write.

Write permission required. Applies one optimistic-concurrency settings update: an expected version plus the optional note and/or auto-renew opt-in; omitted fields stay unchanged. Bounded note validation (at most 200 Unicode characters). Opt-in requires a verified tenant and an active, unexpired, unrefunded instance with renewal support and an original purchase duration. Disabling and note-only edits do not require renewal eligibility. Never a financial command: enabling auto-renew has no wallet effect until the scheduler actually charges, which needs a current active/verified tenant and an active authorizing membership. A stale version is a 409 conflict; a foreign or unknown id is 404.

Path Parameters

Body·

required
application/json

Optimistic-concurrency instance settings update. Omitted note/auto_renew_enabled stay unchanged; a stale expected_version conflicts.

  • The settings_version last read for the instance

  • Replacement auto-renew opt-in; non-financial until the scheduler charges

  • Replacement note

Responses

  • application/json
  • application/json
Request Example for patch/api/customer/v1/proxies/{id}/settings
curl 'https://portal.migoip.com/api/customer/v1/proxies/{id}/settings' \
  --request PATCH \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN' \
  --data '{
  "expected_version": 1,
  "note": "",
  "auto_renew_enabled": true
}'
{
  "service": {
    "id": "string",
    "order_id": "string",
    "operation_id": "string",
    "ip": "string",
    "hostname": "string",
    "product": "static_residential",
    "country": "string",
    "location_id": "string",
    "location_name": "string",
    "location_subdivision": "string",
    "refunded_at": "2026-10-26T05:45:59.177Z",
    "protocol": "http",
    "activated_at": "2026-10-26T05:45:59.177Z",
    "expires_at": "2026-10-26T05:45:59.177Z",
    "term_state": "active",
    "connection_available": true,
    "direct_connection_available": true,
    "optimized_connection_available": true,
    "supported_protocols": [],
    "renewable": true,
    "simulated": true,
    "provenance": "provider",
    "optimization": {
      "selected": true,
      "state": "pending",
      "bandwidth": {
        "upload": {
          "bytes_per_second": "string",
          "burst_bytes": "string"
        },
        "download": {
          "bytes_per_second": "string",
          "burst_bytes": "string"
        }
      }
    },
    "lifecycle": {
      "supported": true,
      "renewal_supported": true,
      "revoke_supported": true,
      "access_state": "string",
      "expires_at": "2026-10-26T05:45:59.177Z",
      "can_renew": true,
      "renewal_block_reason": "string",
      "latest_renewal": {
        "id": "string",
        "service_id": "string",
        "quote_id": "string",
        "branch": "extend",
        "amount_fen": "string",
        "state": "queued",
        "error_code": "string",
        "term_start": null,
        "expires_at": null,
        "created_at": "2026-10-26T05:45:59.177Z",
        "updated_at": "2026-10-26T05:45:59.177Z"
      },
      "prices": [
        {
          "id": "string",
          "product": "static_residential",
          "country": "string",
          "location_id": "string",
          "location_name": "string",
          "location_subdivision": "string",
          "period": "string",
          "days": 1,
          "base_fen": "string"
        }
      ]
    },
    "note": "string",
    "settings_version": 1,
    "auto_renew_enabled": true,
    "auto_renew_period": "string",
    "auto_renew_state": "off",
    "auto_renew_next_attempt_at": "2026-10-26T05:45:59.177Z"
  },
  "connections": [
    {
      "protocol": "http",
      "host": "string",
      "port": 1,
      "username": "string",
      "password": "string",
      "simulated": true
    }
  ],
  "direct_connections": [
    {
      "protocol": "http",
      "host": "string",
      "port": 1,
      "username": "string",
      "password": "string",
      "simulated": true
    }
  ],
  "optimized_connections": [
    {
      "protocol": "http",
      "host": "string",
      "port": 1,
      "username": "string",
      "password": "string",
      "simulated": true
    }
  ]
}

Create a renewal quote

​

Required permissions: orders:write.

Write permission required. Binds the service revision, effective price version and the exact extension arithmetic from the recorded expiry. Read-only: no debit and no provider mutation. An expired, unsupported or manual non-renewable instance returns the existing guard error and no quote.

Path Parameters

Body

required
application/json

Responses

  • application/json
  • application/json
Request Example for post/api/customer/v1/proxies/{id}/renewal-quotes
curl 'https://portal.migoip.com/api/customer/v1/proxies/{id}/renewal-quotes' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN' \
  --data '{
  "price_id": ""
}'
{
  "id": "string",
  "service_id": "string",
  "branch": "extend",
  "price_id": "string",
  "product": "static_residential",
  "country": "string",
  "location_id": "string",
  "location_name": "string",
  "location_subdivision": "string",
  "period": "string",
  "days": 1,
  "optimization_selected": true,
  "base_fen": "string",
  "optimization_fen": "string",
  "total_fen": "string",
  "old_expires_at": "2026-10-26T05:45:59.177Z",
  "proposed_expires_at": null,
  "created_at": "2026-10-26T05:45:59.177Z",
  "expires_at": "2026-10-26T05:45:59.177Z"
}

Pay for a renewal

​

Required permissions: orders:write, wallet:spend.

Write permission required. Debits exactly once with the required Idempotency-Key: wallet debit and renewal row share one transaction and the quote stays bound to the paying actor. An exact replay returns the original renewal; a used quote or changed body conflicts without a debit. The provider mutation is queued for the worker, never performed inline.

Path Parameters

Headers

  • Exact replay returns the original result without spending twice; a changed body conflicts.

Body

required
application/json

Responses

  • application/json
  • application/json
Request Example for post/api/customer/v1/proxies/{id}/renewals
curl 'https://portal.migoip.com/api/customer/v1/proxies/{id}/renewals' \
  --request POST \
  --header 'Idempotency-Key: ' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN' \
  --data '{
  "quote_id": ""
}'
{
  "renewal": {
    "id": "string",
    "service_id": "string",
    "quote_id": "string",
    "branch": "extend",
    "amount_fen": "string",
    "state": "queued",
    "error_code": "string",
    "term_start": null,
    "expires_at": null,
    "created_at": "2026-10-26T05:45:59.177Z",
    "updated_at": "2026-10-26T05:45:59.177Z"
  }
}

Read one renewal status

​

Required permissions: orders:read.

Only the key tenant's own renewal is visible.

Path Parameters

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/proxies/{id}/renewals/{renewal}
curl 'https://portal.migoip.com/api/customer/v1/proxies/{id}/renewals/{renewal}' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "renewal": {
    "id": "string",
    "service_id": "string",
    "quote_id": "string",
    "branch": "extend",
    "amount_fen": "string",
    "state": "queued",
    "error_code": "string",
    "term_start": null,
    "expires_at": null,
    "created_at": "2026-10-26T05:45:59.177Z",
    "updated_at": "2026-10-26T05:45:59.177Z"
  }
}

Quote optimization for an existing static IP

​

Required permissions: orders:write.

Requires write permission and orders:write. Confirm through POST /orders. Existing service expiry is unchanged. Coupons are not supported on upgrades (COUPON_UNAVAILABLE). Unknown body fields are refused.

Path Parameters

Body

application/json

Responses

  • application/json
  • application/json
Request Example for post/api/customer/v1/proxies/{id}/optimization-quotes
curl 'https://portal.migoip.com/api/customer/v1/proxies/{id}/optimization-quotes' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN' \
  --data '{
  "coupon_code": ""
}'
{
  "purchase_kind": "purchase",
  "service_id": "string",
  "service_expires_at": "2026-10-26T05:45:59.177Z",
  "id": "string",
  "tenant_id": "string",
  "price_id": "string",
  "product": "static_residential",
  "country": "string",
  "location_id": "string",
  "location_name": "string",
  "location_subdivision": "string",
  "period": "string",
  "days": 1,
  "quantity": 1,
  "protocol": "http",
  "access_optimized": true,
  "optimization_fen": "string",
  "unit_fen": "string",
  "subtotal_fen": "string",
  "discount_fen": "string",
  "total_fen": "string",
  "coupon": {
    "id": "string",
    "code": "string",
    "name": "string",
    "discount_bps": 1
  },
  "created_at": "2026-10-26T05:45:59.177Z",
  "expires_at": "2026-10-26T05:45:59.177Z",
  "note": "string",
  "auto_renew_enabled": true
}

Quote a batch renewal

​

Required permissions: orders:write.

Requires write permission and orders:write. Accepts 1–100 unique owned service IDs and their exact renewal price IDs. Any ineligible item rejects the entire quote.

Body·

required
application/json
  • Properties: 2

Responses

  • application/json
  • application/json
Request Example for post/api/customer/v1/proxies/batch-renewal-quotes
curl https://portal.migoip.com/api/customer/v1/proxies/batch-renewal-quotes \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN' \
  --data '{
  "items": [
    {
      "service_id": "",
      "price_id": ""
    }
  ]
}'
{
  "id": "string",
  "tenant_id": "string",
  "actor_id": "string",
  "total_fen": "string",
  "created_at": "2026-10-26T05:45:59.177Z",
  "expires_at": "2026-10-26T05:45:59.177Z",
  "quotes": [
    {
      "id": "string",
      "service_id": "string",
      "branch": "extend",
      "price_id": "string",
      "product": "static_residential",
      "country": "string",
      "location_id": "string",
      "location_name": "string",
      "location_subdivision": "string",
      "period": "string",
      "days": 1,
      "optimization_selected": true,
      "base_fen": "string",
      "optimization_fen": "string",
      "total_fen": "string",
      "old_expires_at": "2026-10-26T05:45:59.177Z",
      "proposed_expires_at": null,
      "created_at": "2026-10-26T05:45:59.177Z",
      "expires_at": "2026-10-26T05:45:59.177Z"
    }
  ]
}

Confirm a batch renewal

​

Required permissions: orders:write, wallet:spend.

Requires write permission and orders:write, wallet:spend. One atomic wallet debit; each IP subsequently has its own fulfillment outcome. Reuse the Idempotency-Key on an exact retry.

Headers

  • Exact replay returns the original result without spending twice; a changed body conflicts.

Body

required
application/json

Responses

  • application/json
  • application/json
Request Example for post/api/customer/v1/proxies/batch-renewals
curl https://portal.migoip.com/api/customer/v1/proxies/batch-renewals \
  --request POST \
  --header 'Idempotency-Key: ' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN' \
  --data '{
  "quote_id": ""
}'
{
  "id": "string",
  "tenant_id": "string",
  "quote_id": "string",
  "total_fen": "string",
  "created_at": "2026-10-26T05:45:59.177Z",
  "renewals": [
    {
      "id": "string",
      "service_id": "string",
      "quote_id": "string",
      "branch": "extend",
      "amount_fen": "string",
      "state": "queued",
      "error_code": "string",
      "term_start": null,
      "expires_at": null,
      "created_at": "2026-10-26T05:45:59.177Z",
      "updated_at": "2026-10-26T05:45:59.177Z"
    }
  ]
}

Read a batch renewal

​

Required permissions: orders:read.

Requires orders:read. Foreign and unknown IDs return 404. Internal failure codes are returned as OPERATION_NEEDS_ATTENTION.

Path Parameters

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/proxies/batch-renewals/{batch}
curl 'https://portal.migoip.com/api/customer/v1/proxies/batch-renewals/{batch}' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "id": "string",
  "tenant_id": "string",
  "quote_id": "string",
  "total_fen": "string",
  "created_at": "2026-10-26T05:45:59.177Z",
  "renewals": [
    {
      "id": "string",
      "service_id": "string",
      "quote_id": "string",
      "branch": "extend",
      "amount_fen": "string",
      "state": "queued",
      "error_code": "string",
      "term_start": null,
      "expires_at": null,
      "created_at": "2026-10-26T05:45:59.177Z",
      "updated_at": "2026-10-26T05:45:59.177Z"
    }
  ]
}

Dynamic proxies

​

Read dynamic capabilities and traffic packs

​

Required permissions: dynamic:read.

Requires dynamic:read. Countries, protocols and sticky durations reflect configured support. Disabled products return no sellable packs. Select a pack by its kind: standard or optimization.

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/dynamic/config
curl https://portal.migoip.com/api/customer/v1/dynamic/config \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "tenant_id": "string",
  "ready": true,
  "ready_reason": "string",
  "capabilities": {
    "usage_analytics": {
      "breakdowns": [
        "overview"
      ],
      "intervals": [
        "hour"
      ],
      "max_window_days": 0,
      "bucket_timezone": "string",
      "balance_reporting": true
    },
    "supported_countries": [
      "string"
    ],
    "protocols": [
      "http"
    ],
    "modes": [
      "rotating"
    ],
    "sticky_session_minutes": [
      1
    ],
    "sticky_default_minutes": 0
  },
  "packs": [
    {
      "id": "string",
      "label": "string",
      "kind": "standard",
      "bytes": "string",
      "price_fen": "string",
      "version": 1
    }
  ]
}

Read standard and optimization traffic balances

​

Required permissions: dynamic:read.

Requires dynamic:read. Residential/Usage supplies the standard total/used/remaining snapshot. Reading this data never changes the wallet or traffic allowance. Do not use windowed analytics as cumulative billing usage.

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/dynamic/account
curl https://portal.migoip.com/api/customer/v1/dynamic/account \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "tenant_id": "string",
  "usage_status": "reported",
  "purchased_bytes": "string",
  "adjustment_bytes": "string",
  "balance_status": "reported",
  "balance_source": "local",
  "balance_observed_at": "2026-10-26T05:45:59.177Z",
  "total_bytes": "string",
  "used_bytes": "string",
  "remaining_bytes": "string",
  "status": "none",
  "last_usage_at": "2026-10-26T05:45:59.177Z",
  "optimization": {
    "purchased_bytes": "string",
    "adjustment_bytes": "string",
    "consumed_bytes": "string",
    "reserved_bytes": "string",
    "uncertain_bytes": "string",
    "available_bytes": "string",
    "remaining_bytes": "string",
    "usable_bytes": "string",
    "pending_node_bytes": "string",
    "status": "none",
    "availability_reason": "none"
  }
}

Read windowed dynamic usage analytics

​

Required permissions: dynamic:read.

Requires dynamic:read. Defaults to the last 24 hours, ending at the current UTC minute. Maximum window 30 days, or a lower capability limit. Missing geographic attribution is country unknown, not zero usage. A reporting cooldown returns 429 DYNAMIC_USAGE_RATE_LIMITED with Retry-After.

Query Parameters

  • Inclusive lower bound.

  • Exclusive upper bound; at most one minute in the future.

  • values
    overviewtrendcountrieshosts
  • Maximum ranked host rows.

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/dynamic/usage
curl 'https://portal.migoip.com/api/customer/v1/dynamic/usage?breakdown=overview&interval=hour&limit=20' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "tenant_id": "string",
  "status": "reported",
  "from": "2026-10-26T05:45:59.177Z",
  "to": "2026-10-26T05:45:59.177Z",
  "breakdown": "overview",
  "interval": "hour",
  "bucket_timezone": "string",
  "observed_at": "2026-10-26T05:45:59.177Z",
  "totals": {
    "request_count": "string",
    "limited_count": "string",
    "error_count": "string",
    "upload_bytes": "string",
    "download_bytes": "string",
    "total_bytes": "string"
  },
  "rows": [
    {
      "request_count": "string",
      "limited_count": "string",
      "error_count": "string",
      "upload_bytes": "string",
      "download_bytes": "string",
      "total_bytes": "string",
      "bucket_start": "2026-10-26T05:45:59.177Z",
      "country": "string",
      "host": "string"
    }
  ],
  "capabilities": {
    "breakdowns": [
      "overview"
    ],
    "intervals": [
      "hour"
    ],
    "max_window_days": 0,
    "bucket_timezone": "string",
    "balance_reporting": true
  }
}

Quote a dynamic traffic pack

​

Required permissions: dynamic:write.

Requires write permission and dynamic:write. Uses the workspace-effective pack price and optional eligible coupon.

Body

required
application/json

Responses

  • application/json
  • application/json
Request Example for post/api/customer/v1/dynamic/quotes
curl https://portal.migoip.com/api/customer/v1/dynamic/quotes \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN' \
  --data '{
  "pack_id": "",
  "coupon_code": ""
}'
{
  "id": "string",
  "tenant_id": "string",
  "pack_id": "string",
  "pack_label": "string",
  "kind": "standard",
  "bytes": "string",
  "price_version": 1,
  "subtotal_fen": "string",
  "discount_fen": "string",
  "total_fen": "string",
  "coupon": {
    "id": "string",
    "code": "string",
    "name": "string",
    "discount_bps": 1
  },
  "created_at": "2026-10-26T05:45:59.177Z",
  "expires_at": "2026-10-26T05:45:59.177Z"
}

Purchase a traffic pack

​

Required permissions: dynamic:write, wallet:spend.

Requires write permission and dynamic:write, wallet:spend. Send pack_id, price_version and expected_total_fen from config, or include quote_id and echo the quote’s exact pack/version/total. Prefer a quote to freeze price and use coupons. Exact retries return the same receipt without another debit or supplier grant. A 502 grant result includes the purchase for status tracking; poll it or contact support rather than creating another purchase.

Headers

  • Exact replay returns the original result without spending twice; a changed body conflicts.

Body

required
application/json
  • Integer CNY fen; no binary floating-point money

  • Integer numbers.

Responses

  • application/json
  • application/json
  • application/json
Request Example for post/api/customer/v1/dynamic/purchases
curl https://portal.migoip.com/api/customer/v1/dynamic/purchases \
  --request POST \
  --header 'Idempotency-Key: ' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN' \
  --data '{
  "pack_id": "",
  "price_version": 1,
  "expected_total_fen": "",
  "quote_id": ""
}'
{
  "purchase": {
    "id": "string",
    "tenant_id": "string",
    "pack_id": "string",
    "pack_label": "string",
    "quote_id": "string",
    "kind": "standard",
    "bytes": "string",
    "price_fen": "string",
    "subtotal_fen": "string",
    "discount_fen": "string",
    "coupon": {
      "id": "string",
      "code": "string",
      "name": "string",
      "discount_bps": 1
    },
    "price_version": 1,
    "status": "string",
    "created_at": "2026-10-26T05:45:59.177Z"
  }
}

List dynamic traffic purchases

​

Required permissions: dynamic:read.

Requires dynamic:read. Includes both traffic kinds unless filtered.

Query Parameters

  • Integer numbers.

  • Integer numbers.

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/dynamic/purchases
curl 'https://portal.migoip.com/api/customer/v1/dynamic/purchases?page=1&page_size=20' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "items": [
    {
      "id": "string",
      "tenant_id": "string",
      "pack_id": "string",
      "pack_label": "string",
      "quote_id": "string",
      "kind": "standard",
      "bytes": "string",
      "price_fen": "string",
      "subtotal_fen": "string",
      "discount_fen": "string",
      "coupon": {
        "id": "string",
        "code": "string",
        "name": "string",
        "discount_bps": 1
      },
      "price_version": 1,
      "status": "string",
      "created_at": "2026-10-26T05:45:59.177Z"
    }
  ],
  "total": 0,
  "page": 1,
  "page_size": 1
}

Read dynamic traffic purchase status

​

Required permissions: dynamic:read.

Requires dynamic:read. Unknown and foreign IDs return 404. Provisioning retry, cancellation and wallet refunds remain operator-only.

Path Parameters

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/dynamic/purchases/{id}
curl 'https://portal.migoip.com/api/customer/v1/dynamic/purchases/{id}' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "id": "string",
  "tenant_id": "string",
  "pack_id": "string",
  "pack_label": "string",
  "quote_id": "string",
  "kind": "standard",
  "bytes": "string",
  "price_fen": "string",
  "subtotal_fen": "string",
  "discount_fen": "string",
  "coupon": {
    "id": "string",
    "code": "string",
    "name": "string",
    "discount_bps": 1
  },
  "price_version": 1,
  "status": "string",
  "created_at": "2026-10-26T05:45:59.177Z"
}

List saved dynamic connections

​

Required permissions: dynamic:read.

Requires dynamic:read. Newest first across direct and optimized profiles. The list is bounded by saved-profile limits and always omits credentials (connection is null).

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/dynamic/profiles
curl https://portal.migoip.com/api/customer/v1/dynamic/profiles \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "tenant_id": "string",
  "optimization_available": true,
  "items": [
    {
      "id": "string",
      "tenant_id": "string",
      "country": "string",
      "mode": "rotating",
      "duration_minutes": 1,
      "state": "string",
      "version": 1,
      "created_at": "2026-10-26T05:45:59.177Z",
      "access_optimized": true,
      "connection": {
        "username": "string",
        "password": "string",
        "endpoints": {
          "http": {
            "host": "string",
            "port": 1
          },
          "socks5": {
            "host": "string",
            "port": 1
          }
        }
      }
    }
  ]
}

Generate or reuse a dynamic connection

​

Required permissions: dynamic:write, credentials:read.

Requires write permission and dynamic:write, credentials:read. Use country * for all available countries when advertised by config; country is required. Select rotating or sticky mode and a supported sticky duration. The same effective configuration reuses its saved profile, including under a new idempotency key. Optimized access requires available allowance, gateway and a healthy assigned node. Credential-bearing responses are sensitive.

Headers

  • Exact replay returns the original result without spending twice; a changed body conflicts.

Body

required
application/json
  • ISO country code from config, or * for all available countries.

  • Integer numbers.

Responses

  • application/json
  • application/json
Request Example for post/api/customer/v1/dynamic/profiles
curl https://portal.migoip.com/api/customer/v1/dynamic/profiles \
  --request POST \
  --header 'Idempotency-Key: ' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN' \
  --data '{
  "country": "",
  "protocol": "http",
  "mode": "rotating",
  "duration_minutes": 1,
  "access_optimized": false
}'
{
  "profile": {
    "id": "string",
    "tenant_id": "string",
    "country": "string",
    "mode": "rotating",
    "duration_minutes": 1,
    "state": "string",
    "version": 1,
    "created_at": "2026-10-26T05:45:59.177Z",
    "access_optimized": true,
    "connection": {
      "username": "string",
      "password": "string",
      "endpoints": {
        "http": {
          "host": "string",
          "port": 1
        },
        "socks5": {
          "host": "string",
          "port": 1
        }
      }
    }
  }
}

Read one dynamic connection

​

Required permissions: dynamic:read, credentials:read.

Requires dynamic:read, credentials:read. Connection credentials are returned only for a ready, authorized and entitled profile. Unknown and foreign IDs return 404.

Path Parameters

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/dynamic/profiles/{id}
curl 'https://portal.migoip.com/api/customer/v1/dynamic/profiles/{id}' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "profile": {
    "id": "string",
    "tenant_id": "string",
    "country": "string",
    "mode": "rotating",
    "duration_minutes": 1,
    "state": "string",
    "version": 1,
    "created_at": "2026-10-26T05:45:59.177Z",
    "access_optimized": true,
    "connection": {
      "username": "string",
      "password": "string",
      "endpoints": {
        "http": {
          "host": "string",
          "port": 1
        },
        "socks5": {
          "host": "string",
          "port": 1
        }
      }
    }
  }
}

Remove a saved dynamic connection

​

Required permissions: dynamic:write.

Requires write permission and dynamic:write. Removal is idempotent. Deleting a direct profile removes the saved configuration; it does not rotate shared credentials that were already copied, or refund traffic.

Path Parameters

Responses

  • application/json
  • application/json
Request Example for delete/api/customer/v1/dynamic/profiles/{id}
curl 'https://portal.migoip.com/api/customer/v1/dynamic/profiles/{id}' \
  --request DELETE \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "revoked": true
}

Wallet

​

Read wallet totals

​

Required permissions: wallet:read.

Requires wallet:read. Posted balance, current-month spending and latest recharge use the same consistent snapshot as the portal.

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/wallet/summary
curl https://portal.migoip.com/api/customer/v1/wallet/summary \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "tenant_id": "string",
  "balance_fen": "string",
  "spent_month_fen": "string",
  "month_start": "2026-10-26T05:45:59.177Z",
  "month_end": "2026-10-26T05:45:59.177Z",
  "last_recharge": {
    "id": "string",
    "amount_fen": "string",
    "channel": "manual",
    "created_at": "2026-10-26T05:45:59.177Z"
  }
}

List posted wallet movements

​

Required permissions: wallet:read.

Requires wallet:read. Pending payment intents are listed separately under recharges.

Query Parameters

  • Integer numbers.

  • Integer numbers.

  • Inclusive RFC3339 or YYYY-MM-DD UTC lower bound.

  • Exclusive RFC3339 or YYYY-MM-DD UTC upper bound. Must be later than from.

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/wallet/ledger
curl 'https://portal.migoip.com/api/customer/v1/wallet/ledger?page=1&page_size=20' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "items": [
    {
      "id": "string",
      "tenant_id": "string",
      "kind": "recharge",
      "channel": "manual",
      "direction": "credit",
      "amount_fen": "string",
      "wallet_delta_fen": "string",
      "reason": "string",
      "reference_id": "string",
      "created_at": "2026-10-26T05:45:59.177Z"
    }
  ],
  "total": 0,
  "page": 1,
  "page_size": 1
}

Payments

​

Read available recharge methods

​

Required permissions: recharges:read.

Requires recharges:read. Availability and exact fees follow deployment payment policy. A method being listed does not imply it is enabled.

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/recharges/config
curl https://portal.migoip.com/api/customer/v1/recharges/config \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "tenant_id": "string",
  "currency": "CNY",
  "mock": true,
  "allow_unverified": false,
  "can_create": true,
  "eligibility_reason": "",
  "channels": [
    {
      "channel": "card",
      "enabled": true,
      "policy_revision": "string",
      "flat_fee_fen": "string",
      "fee_bps": "string",
      "fee_rounding": "up",
      "min_amount_fen": "string",
      "max_amount_fen": "string",
      "validity_seconds": 1
    }
  ],
  "disabled_channels": [
    "usdt"
  ]
}

List payment intents

​

Required permissions: recharges:read.

Requires recharges:read. Only limit, offset and state are accepted, each once. Lists omit actionable payment instructions.

Query Parameters

  • Integer numbers.

  • Integer numbers.

  • values
    queuedrunningwaitingneeds_attentionsucceeded

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/recharges
curl 'https://portal.migoip.com/api/customer/v1/recharges?limit=20&offset=0' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "items": [
    {
      "id": "string",
      "tenant_id": "string",
      "actor_id": "string",
      "state": "queued",
      "version": 1,
      "credited": true,
      "mock": true,
      "channel": "card",
      "currency": "CNY",
      "policy_revision": "string",
      "amount_fen": "string",
      "gross_fen": "string",
      "fee_fen": "string",
      "credit_fen": "string",
      "flat_fee_fen": "string",
      "fee_bps": "string",
      "fee_rounding": "up",
      "instructions": {
        "kind": "mock",
        "reference": "string",
        "payment_url": "string"
      },
      "expires_at": "2026-10-26T05:45:59.177Z",
      "created_at": "2026-10-26T05:45:59.177Z"
    }
  ],
  "total": 0,
  "limit": 1,
  "offset": 0
}

Create a recharge payment intent

​

Required permissions: recharges:write.

Requires write permission and recharges:write. New payments require enabled wallet top-up policy. Creating an intent does not credit the wallet; credit follows verified settlement. Repeat an exact command with the same Idempotency-Key. Legacy asset_units input is accepted only for an existing USDT receipt replay; new USDT commands remain disabled.

Headers

  • Exact replay returns the original result without spending twice; a changed body conflicts.

Body

required
application/json

Responses

  • application/json
  • application/json
Request Example for post/api/customer/v1/recharges
curl https://portal.migoip.com/api/customer/v1/recharges \
  --request POST \
  --header 'Idempotency-Key: ' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN' \
  --data '{
  "channel": "card",
  "amount_fen": "",
  "policy_revision": "",
  "expected_fee_fen": "",
  "expected_credit_fen": ""
}'
{
  "id": "string",
  "tenant_id": "string",
  "actor_id": "string",
  "state": "queued",
  "version": 1,
  "credited": true,
  "mock": true,
  "channel": "card",
  "currency": "CNY",
  "policy_revision": "string",
  "amount_fen": "string",
  "gross_fen": "string",
  "fee_fen": "string",
  "credit_fen": "string",
  "flat_fee_fen": "string",
  "fee_bps": "string",
  "fee_rounding": "up",
  "instructions": {
    "kind": "mock",
    "reference": "string",
    "payment_url": "string"
  },
  "expires_at": "2026-10-26T05:45:59.177Z",
  "created_at": "2026-10-26T05:45:59.177Z"
}

Read payment intent and instructions

​

Required permissions: recharges:read.

Requires recharges:read. Payment instructions, including a card client_secret when applicable, appear only for the owning actor while the intent is actionable. These are sensitive single-payment instructions, not confirmation of payment.

Path Parameters

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/recharges/{id}
curl 'https://portal.migoip.com/api/customer/v1/recharges/{id}' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "id": "string",
  "tenant_id": "string",
  "actor_id": "string",
  "state": "queued",
  "version": 1,
  "credited": true,
  "mock": true,
  "channel": "card",
  "currency": "CNY",
  "policy_revision": "string",
  "amount_fen": "string",
  "gross_fen": "string",
  "fee_fen": "string",
  "credit_fen": "string",
  "flat_fee_fen": "string",
  "fee_bps": "string",
  "fee_rounding": "up",
  "instructions": {
    "kind": "mock",
    "reference": "string",
    "payment_url": "string"
  },
  "expires_at": "2026-10-26T05:45:59.177Z",
  "created_at": "2026-10-26T05:45:59.177Z"
}

List recharge and manual adjustment history

​

Required permissions: recharges:read.

Requires recharges:read. Expired uncredited payments have customer display state closed; later verified settlement may still succeed.

Query Parameters

  • Integer numbers.

  • Integer numbers.

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/recharge-history
curl 'https://portal.migoip.com/api/customer/v1/recharge-history?page=1&page_size=20' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "items": [
    {
      "id": "string",
      "tenant_id": "string",
      "kind": "gateway",
      "channel": "manual",
      "direction": "credit",
      "amount_fen": "string",
      "credit_fen": "string",
      "state": "queued",
      "reason": "string",
      "created_at": "2026-10-26T05:45:59.177Z"
    }
  ],
  "total": 0,
  "page": 1,
  "page_size": 1
}

Usage and logs

​

Read workspace metrics

​

Required permissions: metrics:read.

Requires metrics:read. Reports only traffic observed by optimized forwarders. Empty, disabled, stale and unavailable states are distinct from zero traffic. Time windows and sample counts are bounded. Foreign IP IDs return 404 before querying telemetry.

Query Parameters

  • RFC3339 UTC lower bound

  • RFC3339 UTC upper bound

  • Requested sample interval in seconds; adapted upward so the graph returns at most 720 points

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/observability/metrics
curl https://portal.migoip.com/api/customer/v1/observability/metrics \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "status": "ready",
  "reason": "string",
  "observed_at": "2026-10-26T05:45:59.177Z",
  "start": "2026-10-26T05:45:59.177Z",
  "end": "2026-10-26T05:45:59.177Z",
  "step_seconds": 1,
  "coverage": "forwarder_only",
  "points": [
    {
      "timestamp": "2026-10-26T05:45:59.177Z",
      "upload_bps": 1,
      "download_bps": 1,
      "active_connections": 1
    }
  ],
  "totals": {
    "upload_bytes": 1,
    "download_bytes": 1
  }
}

Read workspace logs

​

Required permissions: metrics:read.

Requires metrics:read. Reports only traffic observed by optimized forwarders. Empty, disabled, stale and unavailable states are distinct from zero traffic. Time windows and sample counts are bounded. Foreign IP IDs return 404 before querying telemetry.

Query Parameters

  • RFC3339 UTC lower bound

  • RFC3339 UTC upper bound

  • Maximum number of events returned on one page

  • Opaque cursor bound to the original scope

  • Exact-match protocol filter; no wildcard or pattern syntax

  • Exact-match transport filter. tcp also selects legacy records stored before the field existed (all prior data plane traffic was TCP-only); udp matches only records that explicitly carry udp

  • Exact-match outcome filter

    values
    successerrorcancelledincomplete
  • Exact-match client source address that must parse as IPv4 or IPv6

  • Exact-match destination host filter

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/observability/logs
curl 'https://portal.migoip.com/api/customer/v1/observability/logs?limit=50' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "status": "ready",
  "reason": "string",
  "observed_at": "2026-10-26T05:45:59.177Z",
  "items": [
    {
      "schema_version": 1,
      "event_id": "string",
      "timestamp": "2026-10-26T05:45:59.177Z",
      "event_type": "connection_open",
      "node_id": "string",
      "connection_id": "string",
      "request_id": "string",
      "tenant_id": "string",
      "instance_id": "string",
      "instance_ip": "string",
      "instance_note": "string",
      "assignment_id": "string",
      "source_ip": "string",
      "source_port": 1,
      "destination_host": "string",
      "destination_ip": "string",
      "destination_port": 1,
      "upstream_host": "string",
      "upstream_port": 1,
      "protocol": "http",
      "transport": "tcp",
      "upload_bytes": 1,
      "download_bytes": 1,
      "duration_ms": 1,
      "outcome": "success",
      "failure_stage": "string",
      "close_reason": "string",
      "http_method": "string",
      "http_status": 1
    }
  ],
  "next_cursor": "string",
  "coverage": "forwarder_only"
}

Read one IP’s metrics

​

Required permissions: metrics:read.

Requires metrics:read. Reports only traffic observed by optimized forwarders. Empty, disabled, stale and unavailable states are distinct from zero traffic. Time windows and sample counts are bounded. Foreign IP IDs return 404 before querying telemetry.

Path Parameters

Query Parameters

  • RFC3339 UTC lower bound

  • RFC3339 UTC upper bound

  • Requested sample interval in seconds; adapted upward so the graph returns at most 720 points

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/proxies/{id}/observability/metrics
curl 'https://portal.migoip.com/api/customer/v1/proxies/{id}/observability/metrics' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "status": "ready",
  "reason": "string",
  "observed_at": "2026-10-26T05:45:59.177Z",
  "start": "2026-10-26T05:45:59.177Z",
  "end": "2026-10-26T05:45:59.177Z",
  "step_seconds": 1,
  "coverage": "forwarder_only",
  "points": [
    {
      "timestamp": "2026-10-26T05:45:59.177Z",
      "upload_bps": 1,
      "download_bps": 1,
      "active_connections": 1
    }
  ],
  "totals": {
    "upload_bytes": 1,
    "download_bytes": 1
  }
}

Read one IP’s logs

​

Required permissions: metrics:read.

Requires metrics:read. Reports only traffic observed by optimized forwarders. Empty, disabled, stale and unavailable states are distinct from zero traffic. Time windows and sample counts are bounded. Foreign IP IDs return 404 before querying telemetry.

Path Parameters

Query Parameters

  • RFC3339 UTC lower bound

  • RFC3339 UTC upper bound

  • Maximum number of events returned on one page

  • Opaque cursor bound to the original scope

  • Exact-match protocol filter; no wildcard or pattern syntax

  • Exact-match transport filter. tcp also selects legacy records stored before the field existed (all prior data plane traffic was TCP-only); udp matches only records that explicitly carry udp

  • Exact-match outcome filter

    values
    successerrorcancelledincomplete
  • Exact-match client source address that must parse as IPv4 or IPv6

  • Exact-match destination host filter

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/proxies/{id}/observability/logs
curl 'https://portal.migoip.com/api/customer/v1/proxies/{id}/observability/logs?limit=50' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "status": "ready",
  "reason": "string",
  "observed_at": "2026-10-26T05:45:59.177Z",
  "items": [
    {
      "schema_version": 1,
      "event_id": "string",
      "timestamp": "2026-10-26T05:45:59.177Z",
      "event_type": "connection_open",
      "node_id": "string",
      "connection_id": "string",
      "request_id": "string",
      "tenant_id": "string",
      "instance_id": "string",
      "instance_ip": "string",
      "instance_note": "string",
      "assignment_id": "string",
      "source_ip": "string",
      "source_port": 1,
      "destination_host": "string",
      "destination_ip": "string",
      "destination_port": 1,
      "upstream_host": "string",
      "upstream_port": 1,
      "protocol": "http",
      "transport": "tcp",
      "upload_bytes": 1,
      "download_bytes": 1,
      "duration_ms": 1,
      "outcome": "success",
      "failure_stage": "string",
      "close_reason": "string",
      "http_method": "string",
      "http_status": 1
    }
  ],
  "next_cursor": "string",
  "coverage": "forwarder_only"
}

Referrals

​

Read referral summary

​

Required permissions: referrals:read.

Requires referrals:read. Only the workspace’s own referral program and masked invitee information are exposed.

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/referrals
curl https://portal.migoip.com/api/customer/v1/referrals \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "tenant_id": "string",
  "program_enabled": true,
  "can_generate_link": true,
  "link": {
    "token": "string",
    "enabled": true,
    "version": 1,
    "created_at": "2026-10-26T05:45:59.177Z"
  },
  "first_order_bps": 1,
  "subsequent_order_bps": 1,
  "invited_users": 0,
  "qualifying_orders": 0,
  "earned_fen": "string",
  "reversed_fen": "string",
  "outstanding_recovery_fen": "string"
}

List referral invitees

​

Required permissions: referrals:read.

Requires referrals:read. Server controls attribution, identity masking and reward amounts.

Query Parameters

  • Integer numbers.

  • Integer numbers.

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/referrals/invitees
curl 'https://portal.migoip.com/api/customer/v1/referrals/invitees?page=1&page_size=20' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "items": [
    {
      "id": "string",
      "display_label": "string",
      "registered_at": "2026-10-26T05:45:59.177Z",
      "created_workspace_count": 0,
      "qualifying_orders": 0,
      "earned_fen": "string",
      "reversed_fen": "string"
    }
  ],
  "total": 0,
  "page": 1,
  "page_size": 1
}

List referral rewards

​

Required permissions: referrals:read.

Requires referrals:read. Server controls attribution, identity masking and reward amounts.

Query Parameters

  • Integer numbers.

  • Integer numbers.

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/referrals/rewards
curl 'https://portal.migoip.com/api/customer/v1/referrals/rewards?page=1&page_size=20' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "items": [
    {
      "id": "string",
      "invitee_label": "string",
      "source_kind": "static_purchase",
      "source_reference": "string",
      "attribution_source": "workspace_creator",
      "rate_bps": 0,
      "eligible_paid_fen": "string",
      "earned_fen": "string",
      "reversed_fen": "string",
      "outstanding_recovery_fen": "string",
      "created_at": "2026-10-26T05:45:59.177Z",
      "updated_at": "2026-10-26T05:45:59.177Z"
    }
  ],
  "total": 0,
  "page": 1,
  "page_size": 1
}

Webhooks

​

List webhook subscriptions

​

Required permissions: webhooks:read.

Requires webhooks:read. Includes disabled/deleted subscriptions for delivery history. Signing secrets are omitted.

Query Parameters

  • Integer numbers.

  • Integer numbers.

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/webhooks
curl 'https://portal.migoip.com/api/customer/v1/webhooks?page=1&page_size=20' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "items": [
    {
      "name": "string",
      "url": "https://example.com",
      "event_types": [
        "fulfillment.updated"
      ],
      "enabled": true,
      "wallet_low_balance_threshold_fen": null,
      "id": "string",
      "version": 1,
      "created_at": "2026-10-26T05:45:59.177Z",
      "updated_at": "2026-10-26T05:45:59.177Z",
      "disabled_at": null
    }
  ],
  "total": 0,
  "page": 1,
  "page_size": 1
}

Create a webhook subscription

​

Required permissions: webhooks:write.

Requires write permission and webhooks:write. Maximum 10 enabled subscriptions per workspace. Idempotency-Key is required; exact replay returns the same subscription. Copy the secret securely. Only later committed changes are captured; no historical backfill.

Headers

  • Exact replay returns the original result without spending twice; a changed body conflicts.

Body·

required
application/json
  • values
    fulfillment.updatedrenewal.updatedrefund.completedwallet.low_balancedynamic.purchase.updated
  • HTTPS, port 443 only, no credentials or fragment. Each delivery resolves and pins a public address; private, special and mixed public/private DNS answers are refused.

  • Exact CNY fen; required when wallet.low_balance is selected, null otherwise. Maximum 9007199254740992.

Responses

  • application/json
  • application/json
Request Example for post/api/customer/v1/webhooks
curl https://portal.migoip.com/api/customer/v1/webhooks \
  --request POST \
  --header 'Idempotency-Key: ' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN' \
  --data '{
  "name": "",
  "url": "",
  "event_types": [
    "fulfillment.updated"
  ],
  "enabled": true,
  "wallet_low_balance_threshold_fen": null
}'
{
  "webhook": {
    "name": "string",
    "url": "https://example.com",
    "event_types": [
      "fulfillment.updated"
    ],
    "enabled": true,
    "wallet_low_balance_threshold_fen": null,
    "id": "string",
    "version": 1,
    "created_at": "2026-10-26T05:45:59.177Z",
    "updated_at": "2026-10-26T05:45:59.177Z",
    "disabled_at": null
  },
  "secret": "string"
}

Read a webhook subscription

​

Required permissions: webhooks:read.

Requires webhooks:read. Foreign IDs return 404.

Path Parameters

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/webhooks/{id}
curl 'https://portal.migoip.com/api/customer/v1/webhooks/{id}' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "webhook": {
    "name": "string",
    "url": "https://example.com",
    "event_types": [
      "fulfillment.updated"
    ],
    "enabled": true,
    "wallet_low_balance_threshold_fen": null,
    "id": "string",
    "version": 1,
    "created_at": "2026-10-26T05:45:59.177Z",
    "updated_at": "2026-10-26T05:45:59.177Z",
    "disabled_at": null
  }
}

Update a webhook subscription

​

Required permissions: webhooks:write.

Requires write permission and webhooks:write. Supply current version; stale version returns 409. Omitted fields are unchanged. Remove wallet.low_balance and set its threshold to null together. Deleted subscriptions cannot be re-enabled.

Path Parameters

Body·

required
application/json
  • Integer numbers.

  • values
    fulfillment.updatedrenewal.updatedrefund.completedwallet.low_balancedynamic.purchase.updated
  • HTTPS, port 443 only, no credentials or fragment. Each delivery resolves and pins a public address; private, special and mixed public/private DNS answers are refused.

  • Exact CNY fen; required when wallet.low_balance is selected, null otherwise. Maximum 9007199254740992.

Responses

  • application/json
  • application/json
Request Example for patch/api/customer/v1/webhooks/{id}
curl 'https://portal.migoip.com/api/customer/v1/webhooks/{id}' \
  --request PATCH \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN' \
  --data '{
  "name": "",
  "url": "",
  "event_types": [
    "fulfillment.updated"
  ],
  "enabled": true,
  "wallet_low_balance_threshold_fen": null,
  "version": 1
}'
{
  "webhook": {
    "name": "string",
    "url": "https://example.com",
    "event_types": [
      "fulfillment.updated"
    ],
    "enabled": true,
    "wallet_low_balance_threshold_fen": null,
    "id": "string",
    "version": 1,
    "created_at": "2026-10-26T05:45:59.177Z",
    "updated_at": "2026-10-26T05:45:59.177Z",
    "disabled_at": null
  }
}

Delete a webhook subscription

​

Required permissions: webhooks:write.

Requires write permission and webhooks:write. Soft deletion retains history and cancels pending deliveries. Repeated deletion is harmless. Already dispatched network requests may still arrive.

Path Parameters

Responses

  • application/json
  • application/json
Request Example for delete/api/customer/v1/webhooks/{id}
curl 'https://portal.migoip.com/api/customer/v1/webhooks/{id}' \
  --request DELETE \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "webhook": {
    "name": "string",
    "url": "https://example.com",
    "event_types": [
      "fulfillment.updated"
    ],
    "enabled": true,
    "wallet_low_balance_threshold_fen": null,
    "id": "string",
    "version": 1,
    "created_at": "2026-10-26T05:45:59.177Z",
    "updated_at": "2026-10-26T05:45:59.177Z",
    "disabled_at": null
  }
}

Rotate webhook signing secret

​

Required permissions: webhooks:write.

Requires write permission and webhooks:write. Returns a fresh secret, replacing the previous one for subsequent attempts. This command is not idempotent: do not automatically repeat an uncertain response. An in-flight delivery can still carry the prior signature. If the response is lost, explicitly rotate again and install that new secret.

Path Parameters

Responses

  • application/json
  • application/json
Request Example for post/api/customer/v1/webhooks/{id}/rotate-secret
curl 'https://portal.migoip.com/api/customer/v1/webhooks/{id}/rotate-secret' \
  --request POST \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "webhook": {
    "name": "string",
    "url": "https://example.com",
    "event_types": [
      "fulfillment.updated"
    ],
    "enabled": true,
    "wallet_low_balance_threshold_fen": null,
    "id": "string",
    "version": 1,
    "created_at": "2026-10-26T05:45:59.177Z",
    "updated_at": "2026-10-26T05:45:59.177Z",
    "disabled_at": null
  },
  "secret": "string"
}

List delivery history

​

Required permissions: webhooks:read.

Requires webhooks:read. Events are delivered at least once and may arrive out of order. Deduplicate using event_id. Failed deliveries stop automatically after 8 attempts per round; up to 3 manual retry rounds are allowed.

Path Parameters

Query Parameters

  • Integer numbers.

  • Integer numbers.

Responses

  • application/json
  • application/json
Request Example for get/api/customer/v1/webhooks/{id}/deliveries
curl 'https://portal.migoip.com/api/customer/v1/webhooks/{id}/deliveries?page=1&page_size=20' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "items": [
    {
      "id": "string",
      "event_id": "string",
      "event_type": "fulfillment.updated",
      "state": "pending",
      "attempts": 1,
      "max_attempts": 1,
      "manual_retries": 0,
      "attempt_budget": 1,
      "next_attempt_at": null,
      "last_attempt_at": null,
      "last_status_code": null,
      "last_error_code": "string",
      "delivered_at": null,
      "created_at": "2026-10-26T05:45:59.177Z",
      "payload": {
        "service_id": "string",
        "status": "fulfilled",
        "expires_at": null,
        "activated_at": "2026-10-26T05:45:59.177Z",
        "order_id": null
      }
    }
  ],
  "total": 0,
  "page": 1,
  "page_size": 1
}

Retry a failed delivery

​

Required permissions: webhooks:write.

Requires write permission and webhooks:write. Only failed deliveries on enabled subscriptions can be retried. Required Idempotency-Key makes exact replay a no-op. Creates one additional bounded retry round, retaining the same event ID.

Path Parameters

Headers

  • Exact replay returns the original result without spending twice; a changed body conflicts.

Responses

  • application/json
  • application/json
Request Example for post/api/customer/v1/webhooks/{id}/deliveries/{delivery}/retry
curl 'https://portal.migoip.com/api/customer/v1/webhooks/{id}/deliveries/{delivery}/retry' \
  --request POST \
  --header 'Idempotency-Key: ' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "delivery": {
    "id": "string",
    "event_id": "string",
    "event_type": "fulfillment.updated",
    "state": "pending",
    "attempts": 1,
    "max_attempts": 1,
    "manual_retries": 0,
    "attempt_budget": 1,
    "next_attempt_at": null,
    "last_attempt_at": null,
    "last_status_code": null,
    "last_error_code": "string",
    "delivered_at": null,
    "created_at": "2026-10-26T05:45:59.177Z",
    "payload": {
      "service_id": "string",
      "status": "fulfilled",
      "expires_at": null,
      "activated_at": "2026-10-26T05:45:59.177Z",
      "order_id": null
    }
  }
}

Models

​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​