An investment in knowledge pays the best interest.
Benjamin Franklin

Api

API Reference#

PortalPay’s developer-facing APIs are fronted by Azure API Management (APIM) on a custom domain. All external integrations must call the APIM gateway and authenticate with an APIM subscription key. Wallet identity is resolved at the gateway based on your subscription and propagated to the backend; clients MUST NOT send wallet identity headers.

Admin-only operations in the PortalPay web app use JWT cookies (
markup
cb_auth_token
) with CSRF protections and role checks.
  • Base URL:
    markup
    https://api.pay.ledger1.ai/portalpay
  • Developer Authentication:
    markup
    Ocp-Apim-Subscription-Key: {your-subscription-key}
  • Identity: Wallet is resolved by APIM from your subscription and propagated to the backend; clients do not manage wallet identity
  • Admin/UI Authentication: JWT cookie (
    markup
    cb_auth_token
    ) in PortalPay; used for sensitive write operations
  • Health:
    markup
    GET /portalpay/healthz
    is open and does not require a subscription key
See Authentication & Security:
markup
../auth.md

Quick Start#

  1. Obtain your APIM subscription key (PortalPay Admin → API Subscriptions or directly in APIM)
  2. Set the header in every developer API request (except healthz):
    markup
    Ocp-Apim-Subscription-Key: {your-subscription-key}
  3. Wallet identity is resolved automatically at the gateway based on your subscription; no wallet fields are accepted in requests.

Example:

bash
curl -X GET "https://api.pay.ledger1.ai/portalpay/api/inventory" \
  -H "Ocp-Apim-Subscription-Key: $APIM_SUBSCRIPTION_KEY"

Gateway and Optional Edge#

APIM custom domain is the primary client endpoint. Azure Front Door (AFD) may be configured as an optional/fallback edge. If AFD is used, it injects an internal
markup
x-edge-secret
header that APIM will accept per policy. Direct backend origin access is denied; all developer traffic should use the APIM custom domain.

Endpoint Index#

Legend:

  • Dev: Requires APIM subscription key
  • Admin (JWT): Requires
    markup
    cb_auth_token
    cookie in PortalPay app (role checks, CSRF)

Inventory (Dev)#

  • GET
    markup
    /portalpay/api/inventory
    – List products with filtering/pagination
  • POST
    markup
    /portalpay/api/inventory
    – Create/update product (idempotent by SKU)
  • DELETE
    markup
    /portalpay/api/inventory?id=...
    – Delete product Docs:
    markup
    ./inventory.md

Orders (Dev)#

  • POST
    markup
    /portalpay/api/orders
    – Generate receipt/order from inventory items Docs:
    markup
    ./orders.md

Receipts (Dev + Admin)#

  • GET
    markup
    /portalpay/api/receipts?limit=...
    – List receipts (Dev)
  • GET
    markup
    /portalpay/api/receipts/{id}
    – Get receipt by ID (Dev)
  • GET
    markup
    /portalpay/api/receipts/status?receiptId=...
    – Check payment status (Dev)
  • POST
    markup
    /portalpay/api/receipts/refund
    – Process refund (Admin – JWT)
  • POST
    markup
    /portalpay/api/receipts/terminal
    – Terminal display/complete (Admin – JWT) Docs:
    markup
    ./receipts.md

Shop Configuration (Dev + Admin)#

  • GET
    markup
    /portalpay/api/shop/config
    – Read merchant configuration (Dev)
  • POST
    markup
    /portalpay/api/shop/config
    – Update configuration (Admin – JWT) Docs:
    markup
    ./shop.md

Site Configuration (Public/Dev, environment-dependent)#

  • GET
    markup
    /portalpay/api/site/config
    – Platform defaults (may be public; APIM can enforce in production) Docs:
    markup
    ./shop.md
    (section: GET /portalpay/api/site/config)

Split (Dev + Admin)#

  • GET
    markup
    /portalpay/api/split/deploy
    – Read split configuration (Dev)
  • GET
    markup
    /portalpay/api/split/transactions?limit=...
    – List split transactions (Dev)
  • POST
    markup
    /portalpay/api/split/deploy
    – Configure split (Admin – JWT)
  • POST
    markup
    /portalpay/api/split/withdraw
    – Deprecated – Use client-side PaymentSplitter.release; may return 410/204 Docs:
    markup
    ./split.md

Pricing Configuration (Dev + Admin)#

  • GET
    markup
    /portalpay/api/pricing/config
    – Read pricing configuration (Dev)
  • POST
    markup
    /portalpay/api/pricing/config
    – Update pricing configuration (Admin – JWT) Docs:
    markup
    ../pricing.md

Billing (Dev)#

  • GET
    markup
    /portalpay/api/billing/balance
    – Current wallet balance and usage Docs:
    markup
    ./billing.md

Tax Catalog (Dev)#

  • GET
    markup
    /portalpay/api/tax/catalog
    – Jurisdiction tax catalog Docs:
    markup
    ./tax.md

Reserve (Dev)#

  • GET
    markup
    /portalpay/api/reserve/balances
    – Reserve balances by currency
  • GET
    markup
    /portalpay/api/reserve/recommend
    – Recommended reserve settings Docs:
    markup
    ./reserve.md

Users (Dev)#

  • GET
    markup
    /portalpay/api/users/search
    – Search users by text, XP range, and metrics filters
  • GET
    markup
    /portalpay/api/users/live
    – List currently live/public users Docs:
    markup
    ./users.md

GraphQL (Dev)#

  • GET/POST
    markup
    /portalpay/api/graphql
    – GraphQL endpoint for queries
    • Queries:
      markup
      user
      ,
      markup
      follows
      ,
      markup
      liveUsers
      ,
      markup
      leaderboard
    • Mutations are not available via APIM developer subscriptions; use PortalPay Admin UI for admin operations Docs:
      markup
      ./graphql.md

Health (Public)#

  • GET
    markup
    /portalpay/healthz
    – Health check (no subscription key required) Docs:
    markup
    ./health.md

Authorization Scopes#

APIM products/subscriptions grant scopes that are enforced by the backend. Common scopes:

  • markup
    inventory:read
    ,
    markup
    inventory:write
  • markup
    orders:create
  • markup
    receipts:read
    ,
    markup
    receipts:write
  • markup
    shop:read
  • markup
    split:read
  • markup
    users:read
  • markup
    graphql:read
Requests missing the required scope return
markup
403 Forbidden
.

Rate Limiting and Errors#

Gateway/backend quotas and rate limits apply per subscription/wallet.

  • 401 Unauthorized: Missing/invalid subscription key
  • 403 Forbidden: Insufficient scope or disallowed action
  • 429 Too Many Requests: Rate limit exceeded

Rate limit headers (if enabled):

  • markup
    X-RateLimit-Limit
  • markup
    X-RateLimit-Remaining
  • markup
    X-RateLimit-Reset

429 payload:

json
{ "error": "rate_limited", "resetAt": 1698765432000 }

Best Practices#

  • Use
    markup
    Ocp-Apim-Subscription-Key
    for all developer requests (except healthz)
  • Client requests do not include wallet identity; it is resolved at the gateway per subscription
  • Treat your subscription key like a secret; rotate if compromised
  • For admin writes, perform actions within the PortalPay web app (JWT cookie is handled by the browser)
  • Monitor usage and errors; consider IP allowlists/WAF rules on your side if applicable

  • Authentication & Security:
    markup
    ../auth.md
  • Inventory:
    markup
    ./inventory.md
  • Orders:
    markup
    ./orders.md
  • Receipts:
    markup
    ./receipts.md
  • Shop:
    markup
    ./shop.md
  • Split:
    markup
    ./split.md
  • Users:
    markup
    ./users.md
  • GraphQL:
    markup
    ./graphql.md
  • OpenAPI:
    markup
    ../../public/openapi.yaml
  • Pricing & Subscription Tiers:
    markup
    ../pricing.md