> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.runpayments.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.runpayments.io/_mcp/server.

# Unified Charge Workflow

[`POST /api/terminal/{device_id}/charge-card`](/reference/terminal-api/charge-card/initiate-charge) runs up to four stages in one interaction: **read** → **charge** → **print** (optional) → **email** (optional).

```json
{
  "amount": "25.00",
  "beep": true,
  "include_signature": true,
  "print_receipt": true,
  "send_receipt": true,
  "email": "customer@example.com"
}
```

* **Amount format:** decimal dollars as a string, greater than zero, at most 2 decimal places (e.g., `"19.99"`). Any invalid amount returns `400 {"error": "Field amount is invalid"}`. The same rule applies to `read-card`.
* **`order_id`** is optional; if omitted, one is generated as `<4-digit-random><yymmddHHMMSS>`.
* **Requesting a step the device can't do never fails the transaction.** The charge completes, and the skipped step is tagged in `result.stage_errors`.
* On success, `result` is the Payments API charge response. Fields vary by gateway, so treat the documented fields as representative, not exhaustive.

## Reading `stage_errors`

| Key         | Value                                 | When it appears                                                   |
| ----------- | ------------------------------------- | ----------------------------------------------------------------- |
| `read`      | Error message                         | The read failed or was cancelled. The charge was never attempted. |
| `signature` | Always `"not supported"`              | Capability gap.                                                   |
| `charge`    | Error message                         | A gateway fault prevented the charge — **not** a decline.         |
| `print`     | `"not supported"` or an error message | Capability gap and device errors.                                 |
| `other`     | Error message                         | Anything uncategorized.                                           |

## Handling declines

> **A decline is an outcome, not a failure — and status won't tell you about it**
>
> A declined card returns `status: "succeeded"` with no `stage_errors`. Approval or decline is indicated by **`result`** — the Payments API's own `result` field, carried inside the Terminal API's `result` object. Always check `result` before showing the merchant an approval.
>
> Print and email **still run on a decline** if requested — the merchant receives a receipt for the declined charge. Don't treat "printed" or "emailed" as equivalent to "paid."

`result` values, per the [Payments API](/reference/payments-api/process-charge):

| Value | Meaning  |
| ----- | -------- |
| `A`   | Approved |
| `B`   | Retry    |
| `C`   | Declined |

### Recommended result handling

1. `status` is `failed` or `cancelled` → show the `stage_errors.read` (or other) message; no charge occurred.
2. `status` is `succeeded` → check `result`:
   * `A` (Approved) — show the approval.
   * `C` (Declined) — show the decline.
   * `B` (Retry) — a transient processor issue, not a decline; retry the charge with a new `charge-card` call rather than showing it to the merchant as declined.
3. Check `stage_errors` for `signature` / `print` tags and surface them as non-blocking notices.