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

# Payment outcomes and retries

> Pay the quote the traveler approved, retry without paying twice, and follow the recovery action of every payment you submit.

These rules apply to every payment you submit to Jinko: a Shared Payment Token, and any other payment type checkout lists in `accepted_payment_types`. A [checkout link](https://docs.dev.gojinko.com/guides/checkout-link.md) payment happens on the hosted page and needs none of them.

## Pay the quote the traveler approved

Pass the `quote_id` from the checkout the traveler approved. The payment is then taken against that quote only:

* A `quote_id` that is not one of the trip's quotes is refused with `NOT_FOUND`, and nothing is charged.
* A quote that can no longer be paid, because it passed its `expires_at` or the travelers changed after it was priced, is refused with `QUOTE_EXPIRED`, and nothing is charged. Run checkout again, show the new total, and pay the new `quote_id`.
* Running checkout again returns the same `quote_id` while that quote is still valid and the trip has not changed. When checkout returns a new `quote_id`, a payment that names the earlier one is still charged the earlier total while that quote is valid, never a price the traveler did not see.

A typed submission must include `quote_id`: without it the request is refused with 400 `CREDENTIAL_FORMAT_INVALID`.

## Retries never charge twice

A typed submission to `POST /v1/agent_payment/submit` carries an `Idempotency-Key` header. The header is required: without it the request is refused with 400 `CREDENTIAL_FORMAT_INVALID`. Use 1 to 255 printable ASCII characters and one key per intended payment.

* **The first accepted request wins.** Sending it again with the same key returns that attempt's outcome instead of charging again.
* The key is compared on the trip, the quote, the payment type, the card kind (`customer_card` or `vcc`, for a card), the amount and the currency, not on the credential itself. The same key with the same trip and quote replays the first outcome even when the credential differs: the new credential is never tried. To pay with another credential, use a new key, and only when the outcome allows another attempt.
* The same key with a different trip, quote, payment type, card kind, amount or currency is refused with 409 `IDEMPOTENCY_KEY_REUSED`.
* A request refused before acceptance with 400 `CREDENTIAL_FORMAT_INVALID` does not use up its key: correct it and send it again with the same key, unless the `Idempotency-Key` itself was refused.
* A timeout is not permission to create a new attempt. Send the same request with the same key, or read the attempt, and follow `recovery.action`.

## Read the outcome

A typed submission answers with a `PaymentAttemptOutcome`:

| Field | What to read |
| - | - |
| `payment_attempt_id` | Save this identifier to read the same attempt again. |
| `payment_status` | One of `pending`, `requires_authentication`, `authorized`, `captured`, `declined`, `cancelling`, `cancelled`, `expired`, `failed`. |
| `booking_status`, `booking_ref` | Booking progress and the booking reference when available. Authorization alone does not mean booked. |
| `payment_error` | `{code, message}` or `null`. Branch on the code, not the message. |
| `recovery` | Exactly one `action`, plus `replacement_credential_required`. |
| `recovery_status` | `automatic` or `manual_review`. |
| `quote_expires_at` | The quote deadline, when returned: the same time checkout and the trip's `quote` give as `expires_at`. |
| `retry_after_seconds` | When to poll again, when returned. |
| `authentication` | Customer authentication `url` and `expires_at`, when returned. |
| `money` | Amounts and resolution details described below, when available. |

**200 means a known outcome, including a decline.** It does not mean the payment or booking succeeded. **202 means the attempt is still in progress** (`pending` or `cancelling`). Wait for the `Retry-After` interval, then poll the attempt:

```bash theme={null}
curl 'https://api.gojinko.com/v1/trip/<TRIP_ID>/payment_attempts/<PAYMENT_ATTEMPT_ID>' \
  --header 'X-API-Key: <TENANT_BOOKING_KEY>'
```

The `{trip_id}` in this path accepts either `trip_<n>` or `<n>` (for example, `trip_123` or `123`). A `trip_id` that is not the attempt's trip returns 404 `NOT_FOUND`. Over MCP, read the same attempt as `payment_attempt` on `get_trip`.

Honor the returned polling interval (`Retry-After` / `retry_after_seconds`). Keep reading the same attempt while its action is `poll`, including during cancellation or an unknown provider outcome. Do not submit a second payment to resolve a pending booking.

## Recovery actions

Always obey the returned recovery action. A code alone does not establish that cleanup has finished or that the quote is still usable.

| `recovery.action` | Caller action |
| - | - |
| `poll` | Wait and read the existing attempt. |
| `authenticate` | Send the customer to the returned `authentication.url`. |
| `retry_same_key` | Retry with the same key; correct pre-acceptance validation errors first. |
| `retry_new_key` | Start the permitted new attempt; replace the credential if `replacement_credential_required` is true. |
| `recheckout` | Call checkout again and use its fresh quote and payment options. |
| `contact_support` | Give Jinko the attempt and request identifiers for reconciliation. |
| `stop` | Do not retry this operation. |

Each `payment_error.code` and the action it asks for is listed under [Payment outcome codes](https://docs.dev.gojinko.com/concepts/errors.md#payment-outcome-codes).

## Money outcomes after booking

When Jinko collects the payment, `money` reports `authorized`, `captured`, `released`, `refunded`, and `externally_reimbursed` as Money objects with `display`. Use `display` for presentation. Read `resolution_status` (`pending`, `resolved`, `manual_review`, `external_reimbursement_pending`, or `externally_reimbursed`) and `refund_reference` when available; missing amounts are not evidence of zero money movement.

* **Definitive booking failure before capture:** the authorization is voided and released; the payment becomes `cancelled` after cleanup.
* **Definitive booking failure after capture:** the captured amount is refunded; read `refunded` and `resolution_status: "resolved"`.
* **Partial delivery:** exactly the booked items are captured, with the remainder released; `booking_status` is `partial`.
* **Result cannot be verified:** `RECONCILIATION_REQUIRED` / `manual_review`; contact support. Jinko does not guess whether money moved or a booking succeeded.


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