# Record a payment on the appointment's visit

Records a money, Client Account, loyalty, gift card, membership, or referral-program payment against the visit resolved from `appointment_id`. This records that payment was received (cash, card terminal, cashless, or a loyalty instrument) — Altegio does not move funds through a payment provider on this call. `amount_minor` must not exceed `amount_to_pay_minor`; for `membership`, `amount_minor` is `0` and coverage is reported through `covers_items`. Gift card and membership addressed by `number` use the same anti-enumeration policy as `searchLoyaltyInstrument` (`422 instrument_not_applicable`, shared rate limit). Requires `Idempotency-Key`; a retry returns the original payment instead of recording it twice. A free visit (`amount_to_pay_minor` is `0`) needs no payment; finalize it through `setAppointmentStatus`.

Endpoint: POST /locations/{location_id}/appointments/{appointment_id}/payments
Version: 3.0.0-preview
Security: OAuth2, ApiKey

## Security:

  - `OAuth2` (unknown)
    oauth2 scopes: checkout:capture

  - `ApiKey` (unknown)
    http bearer

## Path parameters:

  - `location_id` (integer, required)
    Identifier of the Location. Verified against the credential on every call — a Location the credential cannot access responds with `404`.

  - `appointment_id` (integer, required)
    Identifier of the appointment.

## Header parameters:

  - `Idempotency-Key` (string, required)
    Client-generated unique key for this logical request (UUID recommended). A retry with the same key and body returns the original result; the same key with a different body fails with `409 idempotency_key_reused`. Keys are retained at least 24 hours and are scoped to credential, Location, and operation; after expiry the value may be reused for a new logical request.

## Request fields (application/json):

  - `type` (string, required)
    Enum: "payment_method", "account", "deposit", "loyalty_card", "loyalty_program", "gift_card", "membership", "referral_program"

  - `payment_method_id` (integer, required)
    From `listPaymentMethods`.

  - `amount_minor` (integer, required)

  - `account_id` (integer, required)
    Account the payment settles to.

  - `card_brand_id` (integer)

  - `installment_count` (integer)

  - `account_id` (integer, required)

  - `deposit_id` (integer, required)
    From `visit.payment_methods`.

  - `loyalty_card_id` (integer, required)
    From `visit.payment_methods`.

  - `loyalty_card_id` (integer, required)

  - `loyalty_program_id` (integer, required)

  - `number` (string)

  - `gift_card_id` (integer)
    From `visit.payment_methods` or `searchLoyaltyInstrument`.

  - `gift_card_id` (integer, required)

  - `membership_id` (integer)
    From `visit.payment_methods` or `searchLoyaltyInstrument`.

  - `membership_id` (integer, required)

  - `referral_program_id` (integer, required)
    From `visit.payment_methods`.

  - `referrer_phone` (string, required)

## Request examples:

  - `Card payment via a configured method` (unknown)

  - `Direct payment to an account` (unknown)

  - `Spend a membership session (no amount - sessions, not money)` (unknown)

  - `Spend a membership that carries no code` (unknown)

  - `Part of the amount from a Client Account` (unknown)

  - `Apply one of the referral programs the client is eligible for` (unknown)

## Response 201:

  - `201` (unknown)
    The registered payment.

## Response 201 fields (application/json):

  - `id` (string, required)

  - `object` (string, required)
    Enum: "payment"

  - `visit_id` (integer | null)

  - `type` (string, required)
    `membership_auto` and `prepaid_hold` are read-only and never accepted by PaymentCreate.
    Enum: "payment_method", "account", "deposit", "loyalty_card", "loyalty_program", "gift_card", "membership", "referral_program", "membership_auto", "prepaid_hold"

  - `amount_minor` (integer, required)
    Payment amount in minor units. For `membership`, this is `0`; covered items are listed in `covers_items`.

  - `fee_minor` (integer | null)
    Acquiring fee represented inside the payment; it is not exposed as a separate payment.

  - `currency` (string, required)
    ISO 4217, server-controlled by the Location.

  - `account_id` (integer | null)

  - `payment_method_id` (integer | null)

  - `status` (string, required)
    Enum: "completed", "refunded"

  - `covers_items` (array)

  - `covers_items.visit_item_id` (integer, required)

  - `covers_items.amount_minor` (integer)

  - `card_brand_id` (integer | null)

  - `installment_count` (integer | null)

  - `refund` (any)

  - `refund.id` (string, required)

  - `refund.object` (string, required)
    Enum: "refund"

  - `refund.payment_id` (string, required)

  - `refund.amount_minor` (integer, required)
    Reversed amount in minor units.

  - `refund.created_at` (string)
    UTC.

  - `created_at` (string, required)
    UTC.

## Response 201 headers (application/json):

  - `RateLimit-Remaining` (integer)
    Requests left in the most constrained policy window that applies to this request.

  - `RateLimit-Reset` (integer)
    On `429`, seconds until the most constrained policy frees up; it equals `Retry-After` there.
On a successful response it reports the **length** of that policy's window rather than the
time left in the current one, so treat it as the window size, not as a countdown — the
counter to pace against is `RateLimit-Remaining`.

  - `RateLimit-Policy` (string)
    Most constrained policy applied to the request, as `<limit>;w=<window in seconds>`.
    Example: 10;w=60

  - `Location` (string)
    URL of the created resource.

  - `X-Request-Id` (string)
    Request identifier for support and log correlation.

## Response 400:

  - `400` (unknown)
    Client error without a more specific registered code (`bad_request` — status default for
bare 400 and unknown 4xx), or protocol/syntax failure (`malformed_request` — broken JSON /
unparseable body only; must be thrown explicitly, never used as a synonym for every 400).
Semantic field validation is `422 validation_failed`, not 400.

## Response 400 fields (application/problem+json):

  - `type` (string, required)
    Stable URL identifying the problem type; links to documentation.

  - `title` (string, required)
    Human-readable summary of the problem type.

  - `status` (integer, required)
    HTTP status code of this response.

  - `detail` (string)
    Human-readable explanation of this occurrence.

  - `instance` (string, required)
    Request identifier for support and log correlation.

  - `code` (string, required)
    Stable machine-readable reason code.

  - `errors` (array)
    Field-level issues (present on validation problems).

  - `errors.code` (string, required)
    Stable machine-readable reason for this field.

  - `errors.pointer` (string, required)
    JSON Pointer for `body`; parameter name for `query`, `path`, or `header`.

  - `errors.message` (string, required)
    Human-readable explanation.

  - `errors.source` (string, required)
    Where the invalid value came from.
    Enum: "body", "query", "path", "header"

## Response 400 headers (application/problem+json):

  - `X-Request-Id` (string)
    Request identifier for support and log correlation.

## Response 401:

  - `401` (unknown)
    The request has no valid Bearer credential (`invalid_token`).

## Response 401 fields (application/problem+json):

  - `type` (string, required)
    Stable URL identifying the problem type; links to documentation.

  - `title` (string, required)
    Human-readable summary of the problem type.

  - `status` (integer, required)
    HTTP status code of this response.

  - `detail` (string)
    Human-readable explanation of this occurrence.

  - `instance` (string, required)
    Request identifier for support and log correlation.

  - `code` (string, required)
    Stable machine-readable reason code.

  - `errors` (array)
    Field-level issues (present on validation problems).

  - `errors.code` (string, required)
    Stable machine-readable reason for this field.

  - `errors.pointer` (string, required)
    JSON Pointer for `body`; parameter name for `query`, `path`, or `header`.

  - `errors.message` (string, required)
    Human-readable explanation.

  - `errors.source` (string, required)
    Where the invalid value came from.
    Enum: "body", "query", "path", "header"

## Response 401 headers (application/problem+json):

  - `WWW-Authenticate` (string)
    Bearer challenge with the URL of the protected resource metadata (RFC 9728 §5.1),
where the client finds the authorization server.
    Example: Bearer resource_metadata="https://api.alteg.io/.well-known/oauth-protected-resource/api/v3"

  - `X-Request-Id` (string)
    Request identifier for support and log correlation.

## Response 403:

  - `403` (unknown)
    The token lacks the required scope (`missing_scope`), the scope is present but the token subject lacks the business permission (`permission_denied`), or the Location has no active license (`subscription_inactive`).

## Response 403 fields (application/problem+json):

  - `type` (string, required)
    Stable URL identifying the problem type; links to documentation.

  - `title` (string, required)
    Human-readable summary of the problem type.

  - `status` (integer, required)
    HTTP status code of this response.

  - `detail` (string)
    Human-readable explanation of this occurrence.

  - `instance` (string, required)
    Request identifier for support and log correlation.

  - `code` (string, required)
    Stable machine-readable reason code.

  - `errors` (array)
    Field-level issues (present on validation problems).

  - `errors.code` (string, required)
    Stable machine-readable reason for this field.

  - `errors.pointer` (string, required)
    JSON Pointer for `body`; parameter name for `query`, `path`, or `header`.

  - `errors.message` (string, required)
    Human-readable explanation.

  - `errors.source` (string, required)
    Where the invalid value came from.
    Enum: "body", "query", "path", "header"

## Response 403 headers (application/problem+json):

  - `WWW-Authenticate` (string)
    Only on `missing_scope`: Bearer challenge with `error="insufficient_scope"`, the scope the
operation requires and the URL of the protected resource metadata (RFC 6750 §3.1, RFC 9728 §5.1).
    Example: Bearer error="insufficient_scope", scope="team_members:read", resource_metadata="https://api.alteg.io/.well-known/oauth-protected-resource/api/v3"

  - `X-Request-Id` (string)
    Request identifier for support and log correlation.

## Response 404:

  - `404` (unknown)
    The resource does not exist or is hidden by tenant policy. Prefer a resource-specific
code (`appointment_not_found`, `location_not_found`, …). Routing/gate/unknown-path 404s
use the generic `resource_not_found` (same shape, so "no pin" and "missing" are not distinguished).

## Response 404 fields (application/problem+json):

  - `type` (string, required)
    Stable URL identifying the problem type; links to documentation.

  - `title` (string, required)
    Human-readable summary of the problem type.

  - `status` (integer, required)
    HTTP status code of this response.

  - `detail` (string)
    Human-readable explanation of this occurrence.

  - `instance` (string, required)
    Request identifier for support and log correlation.

  - `code` (string, required)
    Stable machine-readable reason code.

  - `errors` (array)
    Field-level issues (present on validation problems).

  - `errors.code` (string, required)
    Stable machine-readable reason for this field.

  - `errors.pointer` (string, required)
    JSON Pointer for `body`; parameter name for `query`, `path`, or `header`.

  - `errors.message` (string, required)
    Human-readable explanation.

  - `errors.source` (string, required)
    Where the invalid value came from.
    Enum: "body", "query", "path", "header"

## Response 404 headers (application/problem+json):

  - `X-Request-Id` (string)
    Request identifier for support and log correlation.

## Response 406:

  - `406` (unknown)
    Requested `Accept` media type cannot be satisfied (`not_acceptable`).

## Response 406 fields (application/problem+json):

  - `type` (string, required)
    Stable URL identifying the problem type; links to documentation.

  - `title` (string, required)
    Human-readable summary of the problem type.

  - `status` (integer, required)
    HTTP status code of this response.

  - `detail` (string)
    Human-readable explanation of this occurrence.

  - `instance` (string, required)
    Request identifier for support and log correlation.

  - `code` (string, required)
    Stable machine-readable reason code.

  - `errors` (array)
    Field-level issues (present on validation problems).

  - `errors.code` (string, required)
    Stable machine-readable reason for this field.

  - `errors.pointer` (string, required)
    JSON Pointer for `body`; parameter name for `query`, `path`, or `header`.

  - `errors.message` (string, required)
    Human-readable explanation.

  - `errors.source` (string, required)
    Where the invalid value came from.
    Enum: "body", "query", "path", "header"

## Response 406 headers (application/problem+json):

  - `X-Request-Id` (string)
    Request identifier for support and log correlation.

## Response 409:

  - `409` (unknown)
    The `Idempotency-Key` was already used with a different body (`idempotency_key_reused`), or the first request with this key is still running (`idempotency_request_in_progress`, retry after `Retry-After`).

## Response 409 fields (application/problem+json):

  - `type` (string, required)
    Stable URL identifying the problem type; links to documentation.

  - `title` (string, required)
    Human-readable summary of the problem type.

  - `status` (integer, required)
    HTTP status code of this response.

  - `detail` (string)
    Human-readable explanation of this occurrence.

  - `instance` (string, required)
    Request identifier for support and log correlation.

  - `code` (string, required)
    Stable machine-readable reason code.

  - `errors` (array)
    Field-level issues (present on validation problems).

  - `errors.code` (string, required)
    Stable machine-readable reason for this field.

  - `errors.pointer` (string, required)
    JSON Pointer for `body`; parameter name for `query`, `path`, or `header`.

  - `errors.message` (string, required)
    Human-readable explanation.

  - `errors.source` (string, required)
    Where the invalid value came from.
    Enum: "body", "query", "path", "header"

## Response 409 headers (application/problem+json):

  - `X-Request-Id` (string)
    Request identifier for support and log correlation.

  - `Retry-After` (integer)
    Seconds to wait before retrying.

## Response 415:

  - `415` (unknown)
    Unsupported request media type.

## Response 415 fields (application/problem+json):

  - `type` (string, required)
    Stable URL identifying the problem type; links to documentation.

  - `title` (string, required)
    Human-readable summary of the problem type.

  - `status` (integer, required)
    HTTP status code of this response.

  - `detail` (string)
    Human-readable explanation of this occurrence.

  - `instance` (string, required)
    Request identifier for support and log correlation.

  - `code` (string, required)
    Stable machine-readable reason code.

  - `errors` (array)
    Field-level issues (present on validation problems).

  - `errors.code` (string, required)
    Stable machine-readable reason for this field.

  - `errors.pointer` (string, required)
    JSON Pointer for `body`; parameter name for `query`, `path`, or `header`.

  - `errors.message` (string, required)
    Human-readable explanation.

  - `errors.source` (string, required)
    Where the invalid value came from.
    Enum: "body", "query", "path", "header"

## Response 415 headers (application/problem+json):

  - `X-Request-Id` (string)
    Request identifier for support and log correlation.

## Response 422:

  - `422` (unknown)
    Validation failed, amount exceeds unpaid remainder (`exceeds_unpaid`), or a number-addressed loyalty instrument is not usable (`instrument_not_applicable`).

## Response 422 fields (application/problem+json):

  - `type` (string, required)
    Stable URL identifying the problem type; links to documentation.

  - `title` (string, required)
    Human-readable summary of the problem type.

  - `status` (integer, required)
    HTTP status code of this response.

  - `detail` (string)
    Human-readable explanation of this occurrence.

  - `instance` (string, required)
    Request identifier for support and log correlation.

  - `code` (string, required)
    Stable machine-readable reason code.

  - `errors` (array)
    Field-level issues (present on validation problems).

  - `errors.code` (string, required)
    Stable machine-readable reason for this field.

  - `errors.pointer` (string, required)
    JSON Pointer for `body`; parameter name for `query`, `path`, or `header`.

  - `errors.message` (string, required)
    Human-readable explanation.

  - `errors.source` (string, required)
    Where the invalid value came from.
    Enum: "body", "query", "path", "header"

## Response 422 headers (application/problem+json):

  - `X-Request-Id` (string)
    Request identifier for support and log correlation.

## Response 429:

  - `429` (unknown)
    Rate limit exceeded (`rate_limit_exceeded`). Retry after `Retry-After`.

## Response 429 fields (application/problem+json):

  - `type` (string, required)
    Stable URL identifying the problem type; links to documentation.

  - `title` (string, required)
    Human-readable summary of the problem type.

  - `status` (integer, required)
    HTTP status code of this response.

  - `detail` (string)
    Human-readable explanation of this occurrence.

  - `instance` (string, required)
    Request identifier for support and log correlation.

  - `code` (string, required)
    Stable machine-readable reason code.

  - `errors` (array)
    Field-level issues (present on validation problems).

  - `errors.code` (string, required)
    Stable machine-readable reason for this field.

  - `errors.pointer` (string, required)
    JSON Pointer for `body`; parameter name for `query`, `path`, or `header`.

  - `errors.message` (string, required)
    Human-readable explanation.

  - `errors.source` (string, required)
    Where the invalid value came from.
    Enum: "body", "query", "path", "header"

## Response 429 headers (application/problem+json):

  - `Retry-After` (integer)
    Seconds to wait before retrying.

  - `RateLimit-Remaining` (integer)
    Requests left in the most constrained policy window that applies to this request.

  - `RateLimit-Reset` (integer)
    On `429`, seconds until the most constrained policy frees up; it equals `Retry-After` there.
On a successful response it reports the **length** of that policy's window rather than the
time left in the current one, so treat it as the window size, not as a countdown — the
counter to pace against is `RateLimit-Remaining`.

  - `RateLimit-Policy` (string)
    Most constrained policy applied to the request, as `<limit>;w=<window in seconds>`.
    Example: 10;w=60

  - `X-Request-Id` (string)
    Request identifier for support and log correlation.

## Response 500:

  - `500` (unknown)
    Unexpected server error (`internal_error`).

## Response 500 fields (application/problem+json):

  - `type` (string, required)
    Stable URL identifying the problem type; links to documentation.

  - `title` (string, required)
    Human-readable summary of the problem type.

  - `status` (integer, required)
    HTTP status code of this response.

  - `detail` (string)
    Human-readable explanation of this occurrence.

  - `instance` (string, required)
    Request identifier for support and log correlation.

  - `code` (string, required)
    Stable machine-readable reason code.

  - `errors` (array)
    Field-level issues (present on validation problems).

  - `errors.code` (string, required)
    Stable machine-readable reason for this field.

  - `errors.pointer` (string, required)
    JSON Pointer for `body`; parameter name for `query`, `path`, or `header`.

  - `errors.message` (string, required)
    Human-readable explanation.

  - `errors.source` (string, required)
    Where the invalid value came from.
    Enum: "body", "query", "path", "header"

## Response 500 headers (application/problem+json):

  - `X-Request-Id` (string)
    Request identifier for support and log correlation.

## Response 503:

  - `503` (unknown)
    A dependency required for safe processing (e.g. the idempotency store) is unavailable; the write was **not** performed (`dependency_unavailable`). Retry with the same `Idempotency-Key` after a backoff.

## Response 503 fields (application/problem+json):

  - `type` (string, required)
    Stable URL identifying the problem type; links to documentation.

  - `title` (string, required)
    Human-readable summary of the problem type.

  - `status` (integer, required)
    HTTP status code of this response.

  - `detail` (string)
    Human-readable explanation of this occurrence.

  - `instance` (string, required)
    Request identifier for support and log correlation.

  - `code` (string, required)
    Stable machine-readable reason code.

  - `errors` (array)
    Field-level issues (present on validation problems).

  - `errors.code` (string, required)
    Stable machine-readable reason for this field.

  - `errors.pointer` (string, required)
    JSON Pointer for `body`; parameter name for `query`, `path`, or `header`.

  - `errors.message` (string, required)
    Human-readable explanation.

  - `errors.source` (string, required)
    Where the invalid value came from.
    Enum: "body", "query", "path", "header"

## Response 503 headers (application/problem+json):

  - `X-Request-Id` (string)
    Request identifier for support and log correlation.

## Response 201 examples:

  - `A registered payment` (unknown)

## Response 400 examples:

  - `Generic client reject (status default)` (unknown)

  - `Broken JSON / protocol only` (unknown)

## Response 403 examples:

  - `Token scope is insufficient` (unknown)

  - `Subject lacks the business permission` (unknown)

  - `Location license is not active` (unknown)

## Response 404 examples:

  - `Generic 404 (routing / gate / hidden)` (unknown)

  - `Resource-specific 404` (unknown)

## Response 409 examples:

  - `Same key, different body` (unknown)

  - `First request still in progress` (unknown)

## Response 422 examples:

  - `Amount exceeds the unpaid remainder` (unknown)

  - `Loyalty instrument not usable for this visit` (unknown)

