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#
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#
{
"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
cashdiscountschedule) - ACH price = posted price under a V5
cashdiscountschedule (ACH is cash-equivalent). UnderenforcementMode: legacythe legacy convenience fee is applied to the ACH lane as well, so always displayprices.ach.totalrather than assuming it equals the cash price
Config-only probe#
Check whether a merchant requires dual-pricing display before the operator enters an amount:
Returns dualPricingRequired, enforcementMode and customerFeesEnabled (plus pricingModel when one is resolved) with zeroed price fields and showPreview: false.
Fee Preview — Public API#
What the cardholder will actually be charged, for API-key integrations (checkouts, ERP). Runs the same pricing as the real sale.
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 |
{
"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):
Headers: x-internal-service-key, x-tenant-id
Returns cashDiscountEnabled, dualPricingRequired, v5PricingModel, enforcementMode, and legacy convenienceFee* fields.
POS integration checklist#
- On amount entry (or cart total change), call
fee-previewwithbaseAmountandtipAmount. - If
dualPricingRequired, renderprices.cash.label+prices.cash.totalandprices.card.label+prices.card.total(andprices.achwhen ACH is offered). - When the customer selects a payment method, charge the matching
prices.{lane}.total. - Do not compute adjustments client-side — always use this API so preview matches the sale path.
- Dual pricing needs the merchant's pricing set to the cash discount model;
dualPricingRequiredistruewhen it is.
Related endpoints#
| 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.