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

# Start a checkout

> Create a hosted checkout with a form post, a JSON request, or a reusable launch link.

There are two ways to start a checkout. Both create the checkout and redirect the payer to it.

| Method | Use it for |
| - | - |
| `POST /api/v1/payer/intake` | A checkout built for one payer, from a form on your site or from your server |
| `GET /api/v1/payer/intake/link` | A link you can reuse, for example in an email, a button, or an iframe |

Neither needs an API key. The examples use the sandbox at `https://apistagepc.grogov.us`. The full schemas are in the API Reference: [Start a hosted checkout](/api-reference/hosted-checkout/start-a-hosted-checkout) and [Open a hosted checkout from a launch link](/api-reference/hosted-checkout/open-a-hosted-checkout-from-a-launch-link).

## Post a form

Put a form on your page that posts to the intake. The payer's browser follows the redirect to the checkout. Nest fields with brackets: `items[0][id]`.

```html theme={null}
<form method="POST" action="https://apistagepc.grogov.us/api/v1/payer/intake">
  <input type="hidden" name="portal_id" value="<your-portal-id>" />
  <input type="hidden" name="reference_id" value="INV-10293" />
  <input type="hidden" name="return_url" value="https://partner.example.com/checkout/return" />
  <input type="hidden" name="cancel_url" value="https://partner.example.com/checkout/cancel" />
  <input type="hidden" name="items[0][id]" value="FINE-2026-0042" />
  <input type="hidden" name="items[0][description]" value="Parking citation 0042" />
  <input type="hidden" name="items[0][amount]" value="7500" />
  <input type="hidden" name="items[1][id]" value="LATE-FEE" />
  <input type="hidden" name="items[1][description]" value="Late fee" />
  <input type="hidden" name="items[1][amount]" value="2500" />
  <button type="submit">Pay $100.00</button>
</form>
```

## Send JSON from your server

You can send the same fields as JSON from your backend. Do not follow the redirect: read its `Location` header and send the payer there.

```ts Node.js theme={null}
const res = await fetch("https://apistagepc.grogov.us/api/v1/payer/intake", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  redirect: "manual",
  body: JSON.stringify({
    portal_id: "<your-portal-id>",
    reference_id: "INV-10293",
    payer_email: "jane.doe@example.com",
    return_url: "https://partner.example.com/checkout/return",
    cancel_url: "https://partner.example.com/checkout/cancel",
    items: [
      { id: "FINE-2026-0042", description: "Parking citation 0042", amount: 7500 },
      { id: "LATE-FEE", description: "Late fee", amount: 2500 },
    ],
  }),
});

const checkoutUrl = res.headers.get("location"); // 303: the checkout page, or a failure address
// Redirect the payer's browser to checkoutUrl.
```

The answer is always a `303` redirect, never a JSON body, including on failure. A failure points at your `cancel_url` or the GroGov error page; see [Errors](#errors).

## Use a launch link

A launch link carries the checkout in its query string. Each time someone opens it, a new checkout is created, so you can reuse one link. Anyone who has the link can start a checkout with it.

Number the items from 1 with `item<N>_` fields:

```text theme={null}
https://apistagepc.grogov.us/api/v1/payer/intake/link
  ?portal_id=<your-portal-id>
  &reference_id=INV-10293
  &return_url=https%3A%2F%2Fpartner.example.com%2Fcheckout%2Freturn
  &item1_id=FINE-2026-0042&item1_description=Parking%20citation%200042&item1_amount=7500
  &item2_id=LATE-FEE&item2_description=Late%20fee&item2_amount=2500
```

(The line breaks are for reading. A real link is one line.)

* Each item takes `item<N>_id`, `item<N>_description`, `item<N>_amount`, and optionally `item<N>_quantity` and `item<N>_category`. `item<N>_amount_cents` works in place of `item<N>_amount`. Bracket names such as `items[0][id]` also work.
* For a single item, you can send `description` and `amount`, with optional `item_id` (default `ITEM-001`) and `quantity`.
* The top-level fields are the same as in a post. A launch link cannot carry `metadata`, or the items' `required` and `metadata`.
* URL-encode every value.

### Show the checkout in an iframe

Use the launch link as the iframe's `src` and add `embed=iframe`:

```html theme={null}
<iframe
  src="https://apistagepc.grogov.us/api/v1/payer/intake/link?portal_id=<your-portal-id>&reference_id=INV-10293&description=Parking%20citation%200042&amount=7500&embed=iframe"
  width="100%" height="800" title="Payment">
</iframe>
```

If the agency has set allowed origins for the portal, the checkout only shows inside a page from one of them. Ask for your site's origin to be added, as in [Onboarding](/guides/hosted-checkout/overview#onboarding).

## Fields

| Field | Required | Description |
| - | - | - |
| `portal_id` | Yes | The portal GroGov gave you for hosted checkout (UUID) |
| `reference_id` | Yes | Your reference, 1 to 64 characters. It comes back on the result |
| `label` | No | A heading for the amount, shown to the payer. Up to 80 characters, and no URLs |
| `payer_name` | No | Shown on the confirm step and pre-fills the cardholder name, which the payer can change. The payment records the name the payer submits. Up to 200 characters |
| `payer_email` | No | Stored with the payment when the payer does not enter an email of their own. Not shown to the payer; the emailed receipt goes to the address the payer enters |
| `payer_phone` | No | Stored with the payment when the payer does not enter a phone of their own. Not shown to the payer. Up to 50 characters |
| `return_url` | No | Where the payer goes after paying. `https`, on the portal's allowlist. Defaults to the portal's return address |
| `cancel_url` | No | Where the payer goes when they cancel or the request fails. `https`, on the portal's allowlist. Defaults to the portal's cancel address |
| `expires_at` | No | When the link stops working: ISO 8601, ending in `Z` or a UTC offset, in the future. Defaults to the portal's link lifetime (24 hours unless set otherwise), and a later time is shortened to it |
| `metadata` | No | Your own data, at most 8 KB as JSON. Stored with the checkout and not shown to the payer |
| `items` | Yes | 1 to 100 line items |

Each item:

| Field | Required | Description |
| - | - | - |
| `id` | Yes | Your identifier for the line, 1 to 64 characters |
| `description` | Yes | Shown to the payer. 1 to 200 characters, and no URLs |
| `amount` | Yes | Price of one unit, in integer cents. `7500` is \$75.00 |
| `quantity` | No | Whole number, at least 1. Defaults to 1 |
| `category` | No | Up to 32 characters |
| `required` | No | `true` or `false`, stored with the line. It does not change the checkout: the payer always pays every line |
| `metadata` | No | Your own data for the line, at most 8 KB as JSON |

The total is the sum of `amount` × `quantity` over all items. The service fee is added on top of it at checkout.

## Errors

When a request fails, no checkout is created and the payer is redirected:

* To your `cancel_url` with `error=validation_error`, `detail=<code>`, and a `sig` when the portal has a signing secret. See [Results and signatures](/guides/hosted-checkout/results-and-signatures).
* To the GroGov error page with `?code=<code>` when there is no usable `cancel_url`: no `portal_id` or an unknown one, a `cancel_url` that is not allowed, or a server error.

| Code | Meaning | What to do |
| - | - | - |
| `portal_not_found` | No active portal has this `portal_id`. Always shown on the GroGov error page | Check the `portal_id`, and that you are calling the matching environment |
| `portal_disabled` | Hosted checkout is turned off for this portal, or for the whole environment | Ask the agency or GroGov support to turn it on |
| `origin_not_allowed` | Reserved. Not returned today | None |
| `invalid_payload` | A field is missing or invalid. For example: an item without an `amount`, an amount that is not whole cents, more than 100 items, `metadata` over 8 KB, a URL in a `label` or `description`, or a malformed address. Without a `portal_id`, it is shown on the GroGov error page | Check the request against [Fields](#fields) |
| `missing_reference_id` | `reference_id` is missing, empty, or longer than 64 characters | Send a `reference_id` |
| `amount_exceeds_cap` | The total is above the portal's maximum | Split the checkout, or ask the agency about the maximum |
| `invalid_return_url` | `return_url` or `cancel_url` is not `https`, or is not on the portal's allowlist. If the `cancel_url` is at fault, the payer sees the GroGov error page | Use an allowlisted `https` address, or ask for it to be added |
| `expired_request` | `expires_at` is in the past | Send a future time, or leave it out |
| `server_error` | Something failed on our side. Always shown on the GroGov error page | Try again. If it persists, email `support@grogov.us` |

Only the first problem is reported. Fix it and try again to see the next one.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.