Developer dashboard

Fee Preview API (Dual Pricing / POS)#

Pre-sale price preview for mobile and POS integrators. Returns posted cash price, credit (card) price, and ACH price so the client can display lawful dual pricing before charging (required in jurisdictions such as New York).

Last updated: 2026-10-01

Two endpoints return the same pricing: the JWT route below (dashboard, mobile and POS apps) and the API-key route GET /api/v1/public/fee-preview for server and checkout integrations (see Fee Preview — Public API).


Endpoint#

GET/api/v1/transactions/fee-preview

Authentication#

Header Required Description
Authorization Yes Bearer <user_jwt>
X-Tenant-ID Yes Tenant slug or ID
X-Device-Token Optional Device context

Merchant-scoped users always get their own merchant (any merchantId sent is ignored). Admin users must pass merchantId, otherwise 400 VALIDATION_ERROR (Merchant ID is required for admin users). API-key integrations should call GET /api/v1/public/fee-preview instead.


Query parameters#

Parameter Type Required Description
merchantId UUID Admin only Merchant to preview
baseAmount number Yes* Merchandise amount before tip and before lane adjustment
amount number Alias Same as baseAmount
tipAmount number No Tip (default 0)
transactionType string No sale (default) or auth
branchId UUID No Branch-specific schedule
configOnly boolean No When true, return display config only (no amount math)

*When baseAmount is missing or ≤ 0 (or configOnly=true) no error is returned — the response is the config-only shape with zeroed prices.

Whether customer fees apply is taken from the merchant's primary gateway (serviceFeeActive, default true); it cannot be overridden on this route.


Response#

json
{
  "success": true,
  "data": {
    "baseAmount": 100,
    "tipAmount": 0,
    "cardPrice": 104,
    "cashPrice": 100,
    "achPrice": 100,
    "convenienceFee": 4,
    "pricingModel": "cashdiscount",
    "enforcementMode": "v5",
    "customerFeesEnabled": true,
    "dualPricingRequired": true,
    "showPreview": true,
    "source": "v5",
    "cashDiscount": {
      "cashPrice": 100,
      "cardPrice": 104,
      "adjustment": 4
    },
    "prices": {
      "cash": { "total": 100, "adjustment": 0, "label": "Cash price" },
      "card": { "total": 104, "adjustment": 4, "label": "Credit price" },
      "ach":  { "total": 100, "adjustment": 0, "label": "ACH price" }
    },
    "appliedRules": [
      { "feeCode": "CD_ADJ", "feeName": "Card Adjustment", "calc": "percent", "amount": 4 }
    ],
    "snapshotId": "9b2e4f61-0c3d-4a8e-b7f5-1d2c3e4f5a6b",
    "scheduleVersion": 3
  }
}

Also returned when applicable: perTransactionFee (tech-fee model), canChangePayer / defaultPayer (hybrid freedom model), and feeWarning ({ code, message }). cardPrice / cashPrice / achPrice are deprecated — use prices.*.total.

Fields integrators should use#

Field Use
dualPricingRequired When true, POS must display cash and credit prices before payment
prices.cash / prices.card / prices.ach Per-lane totals and suggested labels
customerFeesEnabled false when merchant disabled customer-facing fees — all lanes equal
enforcementMode legacy = V5 schedule not enforced; dual pricing only active under shadow or v5
showPreview Whether to show any fee breakdown UI

Semantic model (not a “discount”)#

  • Posted / cash price = baseAmount + tipAmount (what the merchant advertises)
  • Credit price = posted price + card adjustment (compiled from V5 cashdiscount schedule)
  • ACH price = posted price under a V5 cashdiscount schedule (ACH is cash-equivalent). Under enforcementMode: legacy the legacy convenience fee is applied to the ACH lane as well, so always display prices.ach.total rather than assuming it equals the cash price

Config-only probe#

Check whether a merchant requires dual-pricing display before the operator enters an amount:

GET/api/v1/transactions/fee-preview?merchantId={id}&configOnly=true

Returns dualPricingRequired, enforcementMode and customerFeesEnabled (plus pricingModel when one is resolved) with zeroed price fields and showPreview: false.


Fee Preview — Public API#

GET/api/v1/public/fee-preview

What the cardholder will actually be charged, for API-key integrations (checkouts, ERP). Runs the same pricing as the real sale.

Fee Previewbash
curl "https://uat.auxcore.net/api/v1/public/fee-preview?amount=49.00&tipAmount=0&paymentMethod=card"   -H "x-api-key: YOUR_API_KEY"

Merchant comes from the API key; tenant-scoped / ERP keys send X-Merchant-ID.

Parameter Type Required Description
amount number Yes Base amount (> 0) — the same number you send to /transaction, never the fee-inclusive total. baseAmount is an alias
tipAmount number No Tip (default 0)
taxAmount number No Tax (default 0)
paymentMethod string No card (default), ach or cash
transactionType string No sale (default) or auth
convenienceFeeActive boolean No Who pays the technology fee. Omit for the merchant's default payer; true — the customer pays it; false — the merchant absorbs it. Honoured when the merchant's account allows changing who pays; otherwise it is ignored, the merchant's pricing decides, and the response carries the warning payer_change_not_allowed. Send the same value as the charge
feeGatewayId string (uuid) No Gateway you will charge on — fee ceilings and floors are per gateway
branchId string (uuid) No Branch-specific schedule
Fee Previewjson
{
  "success": true,
  "data": {
    "baseAmount": 49,
    "convenienceFee": 1.47,
    "totalAmount": 50.47,
    "currency": "USD",
    "paymentMethod": "card",
    "convenienceFeeActive": true,
    "prices": {
      "cash": { "total": 49, "adjustment": 0, "label": "Cash price" },
      "card": { "total": 50.47, "adjustment": 1.47, "label": "Credit price" },
      "ach":  { "total": 49, "adjustment": 0, "label": "ACH price" }
    },
    "pricingModel": "cashdiscount",
    "dualPricingRequired": true
  }
}

convenienceFee and totalAmount are for the requested paymentMethod. warning ({ code, message }) is added when a fee was requested but the quote came back zero, or when convenienceFeeActive was ignored because the merchant's account does not allow changing who pays (payer_change_not_allowed).

HTTP Code Cause
400 VALIDATION_ERROR amount missing or ≤ 0, or no merchant resolvable from the key
401 — Missing or invalid API key
502 FEE_PREVIEW_FAILED Pricing could not be calculated

Internal fee-config (service-to-service)#

Served by the merchant service on its internal mount (not exposed through the API gateway):

GET/internal/merchants/:merchantId/fee-config

Headers: x-internal-service-key, x-tenant-id

Returns cashDiscountEnabled, dualPricingRequired, v5PricingModel, enforcementMode, and legacy convenienceFee* fields.


POS integration checklist#

  1. On amount entry (or cart total change), call fee-preview with baseAmount and tipAmount.
  2. If dualPricingRequired, render prices.cash.label + prices.cash.total and prices.card.label + prices.card.total (and prices.ach when ACH is offered).
  3. When the customer selects a payment method, charge the matching prices.{lane}.total.
  4. Do not compute adjustments client-side — always use this API so preview matches the sale path.
  5. Dual pricing needs the merchant's pricing set to the cash discount model; dualPricingRequired is true when it is.

Endpoint Audience
GET /api/v1/virtual-terminal/fee-preview Dashboard VT (single paymentMethod)
GET /api/v1/public/fee-preview API-key integrations (single paymentMethod, per-lane prices)
GET /api/v1/internal/fee-preview/at-charge Internal services (invoice, payment links); authenticated with x-internal-secret

See Mobile Device Integration for device auth patterns.