> ## 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.

# Idempotency

> Safe retries with Idempotency-Key.

Use an `Idempotency-Key` to prevent duplicate charges or refunds when you retry a request. Reuse the same key and request body for each retry.

## Supported endpoints

* Create a payment with `POST /payments`.
* Refund a payment with `POST /payments/{id}/refund`.
* Void a payment with `POST /payments/{id}/void`.

The header is optional, but strongly recommended on every production charge, refund, and void.

## Sending the header

Pass a unique string (max 255 characters) in the `Idempotency-Key` header. Use a UUID generated with `$(uuidgen)` in shell or `crypto.randomUUID()` in Node. Keys are scoped to your API account.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://apistagepc.grogov.us/api/partner/v1/payments \
    -H "Authorization: ApiKey pc_<your-key>" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: $(uuidgen)" \
    -d '{
      "portalId": "<your-portal-id>",
      "amountCents": 10000,
      "currency": "USD",
      "paymentMethod": { "type": "card", "cardNumber": "4111111111111111", "expirationDate": "12/30", "cvv": "123" },
      "lineItems": [{ "description": "Court fee", "amountCents": 10000, "referenceId": "CASE-2024-001" }]
    }'
  ```

  ```js Node.js theme={null}
  // Generate the key once, before the first attempt, and reuse it on every retry of this charge.
  const idempotencyKey = crypto.randomUUID();

  const res = await fetch("https://apistagepc.grogov.us/api/partner/v1/payments", {
    method: "POST",
    headers: {
      Authorization: "ApiKey pc_<your-key>",
      "Content-Type": "application/json",
      "Idempotency-Key": idempotencyKey,
    },
    body: JSON.stringify({
      portalId: "<your-portal-id>",
      amountCents: 10000, // $100.00
      currency: "USD",
      paymentMethod: { type: "card", cardNumber: "4111111111111111", expirationDate: "12/30", cvv: "123" },
      lineItems: [{ description: "Court fee", amountCents: 10000, referenceId: "CASE-2024-001" }],
    }),
  });
  const data = await res.json();
  ```
</CodeGroup>

## Replay behaviour

If you send the **same key with the same request body**, the API returns the original response without charging or refunding again, with the header:

```text theme={null}
Idempotent-Replayed: true
```

A replay does not create another charge or refund. Error responses (`4xx`) are replayed too.

## Idempotency conflicts

| Code | When | What to do |
| - | - | - |
| `IDEMPOTENCY_MISMATCH` | The key was used before with a different body | Generate a fresh key for a different charge or refund |
| `IDEMPOTENCY_IN_PROGRESS` | The first request with this key is still running | Wait for the interval in `Retry-After` and retry with the same key |
| `IDEMPOTENCY_RECONCILIATION_REQUIRED` | The first **payment** with this key ended in an unknown state (a server or gateway error) | Do not retry with this key. Check whether the payment succeeded (`GET /payments`), and contact GroGov if unsure |

<Note>
  After a `5xx` on **create payment**, the key stays reserved so that a retry cannot charge the card twice. A retry returns `IDEMPOTENCY_RECONCILIATION_REQUIRED`. After a `5xx` on refund or void, the key is released, so you can retry the operation with the same key.
</Note>

## Retrying without a key

<Warning>
  If you do **not** send an `Idempotency-Key` and the request times out or errors, a retry **may charge the cardholder twice**.
</Warning>

## Key expiry

Responses are kept for 24 hours. After that, sending the same key and body is treated as a new request. Generate the key once before your first attempt, store it with the pending charge, and reuse it on retries.


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