> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vibepay.mn/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> From terminal credentials to a live charge and a refund, in five steps.

This walks the exact loop we recommend for verifying an integration: take a real ₮100 payment,
confirm it, then give it straight back. It takes about ten minutes and costs nothing.

<Note>
  There is no sandbox. Vibepay charges real wallets holding real employer money, so a fake
  environment would be a fake test. Use a small amount and reverse it — see
  [Environments](/environments) for why.
</Note>

<Steps>
  <Step title="Create a terminal in the merchant dashboard">
    Sign in to [merchant.vibepay.mn](https://merchant.vibepay.mn), open **Terminals**, and create
    one. Name it after the till it belongs to.

    You will get back a username and a password:

    ```
    username  term_a7f3k9d2
    password  vpt_Kd8sPq2mXn5wLr7tYv1zBc4hGj6fNa0e
    ```

    <Warning>
      The password is shown **once** and is never retrievable again. Copy it now. If you lose it,
      rotate the terminal — that issues a new password and kills the old one immediately.
    </Warning>
  </Step>

  <Step title="Check the credentials work">
    There is no ping endpoint, so ask for one transaction instead. A `200` means you are
    authenticated.

    ```bash theme={null}
    curl -i -u 'term_a7f3k9d2:vpt_Kd8sPq2mXn5wLr7tYv1zBc4hGj6fNa0e' \
      'https://api.vibepay.mn/v1/transactions?limit=1'
    ```

    A `401` means the pair is wrong, unknown, or the terminal is suspended — the response is
    identical in all three cases, on purpose.
  </Step>

  <Step title="Scan the cardholder's QR code">
    Ask the customer to open their Vibepay pass in Apple Wallet or Google Wallet. Scan the QR code
    with whatever scanner your POS already uses and take the decoded string **exactly as it comes
    off the scanner**. It looks like this:

    ```
    vqr_AXk9Lm0pQr2sTu4vWx6yZa8bCd0eFg2h.Ij4kLm6nOp8qRs0t
    ```

    Do not trim it, parse it, or store it. It is single-use and it is about to be spent.
  </Step>

  <Step title="Charge ₮100">
    ```bash theme={null}
    curl -i -u 'term_a7f3k9d2:vpt_Kd8sPq2mXn5wLr7tYv1zBc4hGj6fNa0e' \
      -H 'Content-Type: application/json' \
      -H 'Idempotency-Key: quickstart-001' \
      -d '{"qrToken":"vqr_AXk9Lm0pQr2sTu4vWx6yZa8bCd0eFg2h.Ij4kLm6nOp8qRs0t","amountMNT":100}' \
      https://api.vibepay.mn/v1/transactions/charge-by-token
    ```

    ```json 201 Created theme={null}
    {
      "id": "tx_01k2y7v9j0e8ra7cx3mbq4d5nf",
      "amountMNT": 100,
      "status": "COMPLETED",
      "type": "CHARGE",
      "vatReceiptID": "",
      "vatStatus": "pending",
      "terminalID": "ter_01k2y7v8t5f3s9wq1mzd7b6cxa",
      "createdAt": "2026-08-18T09:14:22.481739Z"
    }
    ```

    **Save the `id`.** It is the only handle you will ever have for refunding this payment.

    `vatStatus: "pending"` is normal and expected — the receipt is being minted in the background.
  </Step>

  <Step title="Refund it">
    ```bash theme={null}
    curl -i -u 'term_a7f3k9d2:vpt_Kd8sPq2mXn5wLr7tYv1zBc4hGj6fNa0e' \
      -X POST \
      https://api.vibepay.mn/v1/transactions/tx_01k2y7v9j0e8ra7cx3mbq4d5nf/reverse
    ```

    `204 No Content` and an empty body. The ₮100 is back in the customer's wallet.

    Call it a second time and you get `409` — the charge is already reversed. That is not a
    failure; it means the refund landed.
  </Step>
</Steps>

## Try the same thing wrong

Two failures worth causing on purpose before you ship, because both will happen in production:

<CodeGroup>
  ```bash Reuse a spent code theme={null}
  # Charge with the same qrToken twice — the code was retired by the first charge.
  # → 409  qr token already used
  ```

  ```bash Wrong field name theme={null}
  curl -u 'term_…:vpt_…' -H 'Content-Type: application/json' \
    -d '{"qrToken":"vqr_…","amountMnt":100}' \
    https://api.vibepay.mn/v1/transactions/charge-by-token
  # → 400  invalid request body
  # The field is amountMNT. A lowercase "nt" is an unknown field, and unknown
  # fields are rejected rather than ignored — otherwise this would charge ₮0.
  ```
</CodeGroup>

## Next

<CardGroup cols={2}>
  <Card title="Handle declines properly" icon="triangle-exclamation" href="/errors">
    The full status and code map, and which failures are worth retrying.
  </Card>

  <Card title="Never double-charge" icon="fingerprint" href="/idempotency">
    How to make a timeout safe to retry.
  </Card>
</CardGroup>
