kepa
GETTING STARTED

Tipping Flows

Three patterns for collecting gratuity, one API surface.

Atlas supports three distinct tipping patterns without adding any tipping-specific endpoints. Which one you pick depends on how your vertical operates: quick-service wants tip at the terminal, full-service restaurants want pre-auth with tip adjustment on capture, and legacy US full-service wants tip on the printed receipt. Each pattern is certified with the schemes and each uses the existing transaction endpoints.

Pattern 1 — Tip at the terminal (quick-service)

The cardholder is standing at the terminal. Prompt them for a tip before the sale, compute the total, and run a single sale for the full amount. This is the default for cafes, counter-service, retail, and most modern POS integrations.

  • ProsSingle call. No tip adjustment risk. Cardholder sees the final total before approving.
  • ConsRequires the cardholder to make a tipping decision in front of the terminal — slower line-at-counter throughput.
  • When to useCafes, bars, fast-casual, any vertical where the cardholder is at the terminal when paying.
pattern 1 — tip at terminal
// 1. Ask the cardholder to pick a tip on the terminal.
const subtotal = 2500; // $25.00

const tipChoice = await atlas.device.input({
  prompt: "Would you like to add a tip?",
  inputType: "SELECTION",
  options: ["15% ($3.75)", "18% ($4.50)", "20% ($5.00)", "Custom", "No tip"],
  timeoutMs: 20000,
});

let tipAmount = 0;
switch (tipChoice.value) {
  case "15% ($3.75)": tipAmount = Math.round(subtotal * 0.15); break;
  case "18% ($4.50)": tipAmount = Math.round(subtotal * 0.18); break;
  case "20% ($5.00)": tipAmount = Math.round(subtotal * 0.20); break;
  case "Custom": {
    const custom = await atlas.device.input({
      prompt: "Enter tip amount in cents",
      inputType: "NUMERIC",
      maxLength: 6,
    });
    tipAmount = parseInt(custom.value ?? "0", 10);
    break;
  }
  default: tipAmount = 0;
}

// 2. Run the sale with the full amount including the tip.
const result = await atlas.transactions.sale({
  amount: subtotal + tipAmount,
  currency: "NZD",
  tipAmount,
  referenceId: "POS-INV-20260409-001",
});

Pattern 2 — Pre-auth + tip-adjust on capture (restaurants)

Run a pre-authorization for the subtotal plus a 20% buffer. The cardholder leaves, the server brings the slip, the customer writes a tip, and the server captures the adjusted amount. This is the canonical US restaurant flow and the only way to cleanly support cardholders who pay and then tip separately.

  • ProsMatches how hospitality actually works. Cardholder leaves before tipping. Supports partial captures under the hold.
  • ConsTwo API calls. Requires back-office discipline to capture within the hold window (typically 7 days).
  • When to useFull-service restaurants, bars with tabs, hotels, any flow where the cardholder leaves before the final amount is known.
pattern 2 — pre-auth + capture
// 1. Hotel or restaurant pre-auth for the subtotal + 20% buffer.
const subtotal = 5000; // $50.00 meal
const buffer   = Math.round(subtotal * 0.20);

const hold = await atlas.transactions.preAuth({
  amount: subtotal + buffer,
  currency: "NZD",
  referenceId: "RST-TAB-4242",
});

// 2. Cardholder leaves. Server brings the printed receipt with a tip line.
//    Back at the POS, the server keys in the tip they wrote on the slip.
const tipAmount = 1000; // $10.00

// 3. Capture the actual final amount (can be higher or lower than the hold).
const capture = await atlas.transactions.preAuthComplete({
  amount: subtotal + tipAmount,
  currency: "NZD",
  originalTransactionId: hold.transactionId,
  referenceId: "RST-TAB-4242",
});

Pattern 3 — Tip on receipt (legacy US)

Approve the subtotal immediately, print a receipt with a blank TIP: line, and run a tip adjustment later as a separate capture. Older US POS systems relied on this pattern because pre-auth + adjust wasn't universally supported. Most new deployments should prefer pattern 2.

  • ProsSimple — no pre-auth state to track. Sale is approved immediately at the original amount.
  • ConsTip adjustment must be applied within 24 hours and before settlement. Cardholder never sees or approves the tip on the terminal. Some schemes discourage this pattern for contactless.
  • When to useLegacy US restaurant integrations. Avoid for new builds unless your acquirer specifically requires it.
pattern 3 — tip on receipt
// 1. Approve the subtotal immediately.
const approved = await atlas.transactions.sale({
  amount: 2500,
  currency: "NZD",
  tipAmount: 0,
  referenceId: "POS-CHK-20260409-001",
});

// 2. Server prints a receipt with a TIP: ___ line. Cardholder writes an amount.
//    Later (within 24h, before settlement) the server runs a tip adjustment.
const adjust = await atlas.transactions.tipAdjust({
  originalTransactionId: approved.transactionId,
  tipAmount: 500,             // $5.00 tip
  referenceId: "POS-CHK-20260409-001-TIP",
});

// The original sale is updated in place: total becomes 2500 + 500 = 3000.
// The settlement batch reflects the adjusted total, not the original.
console.log(adjust.status);   // "APPROVED"
console.log(adjust.amount);   // 3000 (original + tip)

Receipt disclosure

However you collect the tip, the receipt must show the base amount, the tip amount, and the total on three separate lines. The terminal already includes all three on receiptData. See Receipt Requirements for the full rendering contract.