# Payments

Payments collected for Invoices and Patients.

**Required API scopes:** `payments.create`, `payments.read`, `refunds.create`

## List Payments

`GET /v1/payments`

Returns Payments in the authenticated Organization. Filter optionally by `patient_id` and by a `paid_at` window. Each Payment includes `invoice_id`, `patient_id`, and nested `refunds`.

**Required API scopes:** `payments.read`

## Parameters

- `patient_id` (query, `string`) - Filter by a Patient with an active connection to the Organization.
- `paid_at_from` (query, `string`) - Inclusive lower bound for `paid_at` in UTC ISO 8601 format (YYYY-MM-DDTHH:MM:SSZ). Payments with a null `paid_at` are omitted when this filter is set.
- `paid_at_to` (query, `string`) - Inclusive upper bound for `paid_at` in UTC ISO 8601 format (YYYY-MM-DDTHH:MM:SSZ). Payments with a null `paid_at` are omitted when this filter is set.
- `limit` (query, `integer`) - The maximum number of items to return. Defaults to `25`; the maximum is `100`.
- `starting_after` (query, `string`) - Return items after this resource ID. You cannot use this with `cursor`.
- `cursor` (query, `string`) - The `next_cursor` value from the previous page. You cannot use this with `starting_after`.

## Response `200`

Paginated list of `Payment` objects.

- `any`

### Example

```json
{
  "object": "list",
  "data": [
    {
      "id": "00000000-0000-4000-8000-000000000026",
      "object": "payment",
      "amount": 25000,
      "created_at": "2026-01-01T09:00:00Z",
      "currency": "GBP",
      "internal_notes": "Asked about evening appointments with Dr Smith.",
      "invoice_id": "00000000-0000-4000-8000-000000000027",
      "links": {
        "patient": "https://api.carebit.co/v1/patients/00000000-0000-4000-8000-000000000004"
      },
      "paid_at": "2026-01-01T09:00:00Z",
      "patient_id": "00000000-0000-4000-8000-000000000004",
      "payment_method_id": "00000000-0000-4000-8000-000000000036",
      "payment_method_type": "bacs_debit",
      "payor_type": "insurance_company",
      "refunds": [
        {
          "id": "d3d1640d-4d73-43b4-81cb-c0dd11bb3a12",
          "object": "refund",
          "amount": 25000,
          "created_at": "2026-01-01T09:00:00Z",
          "currency": "GBP",
          "error": "The destination payment account declined the refund.",
          "invoice_id": "00000000-0000-4000-8000-000000000027",
          "links": {
            "patient": "https://api.carebit.co/v1/patients/00000000-0000-4000-8000-000000000004"
          },
          "next_steps": null,
          "notes": "Please confirm the appointment by email.",
          "patient_id": "00000000-0000-4000-8000-000000000004",
          "payment_id": "00000000-0000-4000-8000-000000000026",
          "reason": "other",
          "refund_source": "bank_account",
          "status": "canceled",
          "succeeded_at": "2026-01-01T09:00:00Z",
          "updated_at": "2026-01-01T09:00:00Z"
        }
      ],
      "status": "awaiting_authentication",
      "updated_at": "2026-01-01T09:00:00Z"
    }
  ],
  "has_more": false,
  "next_cursor": "eyJzdGFydF90aW1lIjoiMjAyNi0wMS0wMVQwOTowMDowMFoifQ",
  "url": "/v1/payments"
}
```

## Response `400`

A time filter or pagination parameter is invalid.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `401`

The access token is missing, invalid, expired, or revoked.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `403`

The access token lacks the required scope, or the project is disabled.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `404`

The Patient was not found in the Organization.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `429`

Too many requests. Retry after the delay indicated by `Retry-After`.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Code samples

```bash
curl -X GET "https://api.carebit.co/v1/payments" \
  -H "Authorization: Bearer $CAREBIT_ACCESS_TOKEN"
```

```javascript
const response = await fetch("https://api.carebit.co/v1/payments", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.CAREBIT_ACCESS_TOKEN}`,
  },
});

if (!response.ok) {
  throw new Error(`Carebit API error: ${response.status}`);
}

const data = await response.json();
```

```python
import os
import requests

response = requests.get(
    "https://api.carebit.co/v1/payments",
    headers={
        "Authorization": f"Bearer {os.environ['CAREBIT_ACCESS_TOKEN']}",
    }
)
response.raise_for_status()
data = response.json()
```

```ruby
require "httparty"

response = HTTParty.get(
  "https://api.carebit.co/v1/payments",
  headers: {
    "Authorization" => "Bearer #{ENV.fetch("CAREBIT_ACCESS_TOKEN")}"
  }
)
raise "Carebit API error: #{response.code}" unless response.success?
data = response.parsed_response
```

```php
<?php

$client = new GuzzleHttp\Client();

$response = $client->get("https://api.carebit.co/v1/payments", [
    "headers" => [
      "Authorization" => "Bearer " . getenv("CAREBIT_ACCESS_TOKEN"),
    ]
]);
$data = json_decode((string) $response->getBody(), true, flags: JSON_THROW_ON_ERROR);
```


## Create a Payment

`POST /v1/payments`

Creates a card Payment for a Patient with an active connection to the Organization. Requires `payments.create`. Amounts are minor currency units. `payor_type` must be `patient`. The Patient must already have an active stored card PaymentMethod that this Organization can access. Supply `payment_method_id` to charge a specific card from GET /v1/patients/{id}/payment_methods. When `payment_method_id` is omitted, Carebit charges the Patient's default stored card. If the Patient has no stored card, the request fails. Send the Patient to `patient_portal_add_payment_method_url` on the Patient object so they can add a card in the Patient Portal. If Stripe requires extra authentication, Carebit emails the Patient a Patient Portal link to complete it. Other payment method types are not accepted.

**Required API scopes:** `payments.create`

## Parameters

- `Idempotency-Key` (header, `string`) (required) - Client-generated idempotency key. Required for every POST/PATCH write. Replay of the same key with the same body returns the stored response with an `Idempotency-Replayed: true` header. Same key + different body returns `422 idempotency_key_reused`. A duplicate that arrives while the first request is still in flight returns `409 idempotency_conflict` with `Retry-After: 1`.

## Request body (`application/json`)

- `object`
  - `amount` (`integer`) - The Payment amount in minor currency units.
  - `clinician_id` (`string | null`) - format: `uuid`; The optional Clinician associated with the Payment. Defaults to the Invoice's Clinician when `invoice_id` is set.
  - `internal_notes` (`string | null`) - Internal notes recorded with the Payment.
  - `invoice_id` (`string | null`) - format: `uuid`; The optional Invoice to allocate this Payment to. The Invoice must belong to the Patient.
  - `patient_id` (`string`) - format: `uuid`; The Patient with an active connection to the Organization.
  - `payment_method_id` (`string | null`) - format: `uuid`; The stored card PaymentMethod to charge. The PaymentMethod must belong to the Patient and this Organization must have ResourcePermissions to access it. When omitted, Carebit charges the Patient's default stored card. List available cards with GET /v1/patients/{id}/payment_methods.
  - `payment_method_type` (`string`) - enum: `card`; Must be `card`. Carebit charges a stored card PaymentMethod that this Organization can access. Supply `payment_method_id` to choose a specific card, or omit it to charge the default. The Patient must already have an active stored card. If they do not, send them to `patient_portal_add_payment_method_url` on the Patient object. Other payment method types are not accepted.
  - `payor_type` (`string`) - enum: `patient`; The party who made the Payment. Currently only `patient` is supported.

### Example

```json
{
  "amount": 25000,
  "invoice_id": "00000000-0000-4000-8000-000000000027",
  "patient_id": "00000000-0000-4000-8000-000000000004",
  "payment_method_id": "00000000-0000-4000-8000-000000000036",
  "payment_method_type": "card"
}
```

## Response `201`

The requested `Payment`.

- `object`
  - `amount` (`integer`) - The Payment amount in minor currency units.
  - `created_at` (`string`) - format: `date-time`; An ISO 8601 timestamp in UTC, with a `Z` suffix. For example, `2026-07-01T09:00:00Z`.
  - `currency` (`string | null`) - The ISO 4217 currency code.
  - `id` (`string`) - format: `uuid`; The resource's unique identifier, formatted as an RFC 4122 version 4 UUID.
  - `internal_notes` (`string | null`) - Internal notes recorded with the Payment.
  - `invoice_id` (`string | null`) - format: `uuid`; The Invoice this Payment is allocated to, when one is assigned.
  - `links` (`object`) - URLs to related resources.
    - `patient` (`string | null`) - format: `uri`; The full URL of a related resource.
  - `object` (`any`) - Discriminator value emitted at `object`.
  - `paid_at` (`string | null`) - format: `date-time`; An ISO 8601 timestamp in UTC, with a `Z` suffix. For example, `2026-07-01T09:00:00Z`.
  - `patient_id` (`string | null`) - format: `uuid`; The Patient associated with the Payment.
  - `payment_method_id` (`string | null`) - format: `uuid`; The stored card PaymentMethod charged for this Payment, when one was used.
  - `payment_method_type` (`string | null`) - enum: `bacs_debit`, `bank_transfer`, `card`, `card_external`, `cash`, `cheque`, `klarna`, `other`, `standing_order`, `null`; How the Payment was collected.
  - `payor_type` (`string | null`) - enum: `alternative_payor`, `insurance_company`, `location`, `patient`, `null`; The type of party who made the Payment.
  - `refunds` (`array`) - Refunds issued against this Payment, if any. Ordered by `created_at`.
    - `items` (`object`)
      - `amount` (`integer`) - The refunded amount in minor currency units.
      - `created_at` (`string`) - format: `date-time`; An ISO 8601 timestamp in UTC, with a `Z` suffix. For example, `2026-07-01T09:00:00Z`.
      - `currency` (`string | null`) - The ISO 4217 currency code.
      - `error` (`string | null`) - The failure message when the Refund did not complete.
      - `id` (`string`) - format: `uuid`; The resource's unique identifier, formatted as an RFC 4122 version 4 UUID.
      - `invoice_id` (`string | null`) - format: `uuid`; The Invoice associated with the refunded Payment, when one is assigned.
      - `links` (`object`) - URLs to related resources.
        - `patient` (`string | null`) - format: `uri`; The full URL of a related resource.
      - `next_steps` (`string | null`) - Guidance for completing the Refund after a Stripe refund to the original payment method fails. Null unless `status` is `failed` and `refund_source` is `payment_account`.
      - `notes` (`string | null`) - Internal notes recorded with the Refund.
      - `object` (`any`) - Discriminator value emitted at `object`.
      - `patient_id` (`string | null`) - format: `uuid`; The Patient associated with the Refund.
      - `payment_id` (`string | null`) - format: `uuid`; The Payment this Refund was issued against.
      - `reason` (`string | null`) - enum: `other`, `other_party_will_pay`, `overcharged`, `overpaid`, `service_not_delivered`, `null`; The reason the Refund was issued.
      - `refund_source` (`string | null`) - enum: `bank_account`, `deduction_from_balance`, `payment_account`, `null`; How the Refund is paid.
      - `status` (`string | null`) - enum: `canceled`, `failed`, `processing`, `requires_action`, `succeeded`, `null`; The Refund's current status.
      - `succeeded_at` (`string | null`) - format: `date-time`; An ISO 8601 timestamp in UTC, with a `Z` suffix. For example, `2026-07-01T09:00:00Z`.
      - `updated_at` (`string`) - format: `date-time`; An ISO 8601 timestamp in UTC, with a `Z` suffix. For example, `2026-07-01T09:00:00Z`.
  - `status` (`string | null`) - enum: `awaiting_authentication`, `being_processed`, `canceled`, `disputed`, `failed`, `paid`, `partially_refunded`, `refunded`, `scheduled`, `unpaid`, `null`; The Payment's current status.
  - `updated_at` (`string`) - format: `date-time`; An ISO 8601 timestamp in UTC, with a `Z` suffix. For example, `2026-07-01T09:00:00Z`.

### Example

```json
{
  "id": "00000000-0000-4000-8000-000000000026",
  "object": "payment",
  "amount": 25000,
  "created_at": "2026-01-01T09:00:00Z",
  "currency": "GBP",
  "internal_notes": "Asked about evening appointments with Dr Smith.",
  "invoice_id": "00000000-0000-4000-8000-000000000027",
  "links": {
    "patient": "https://api.carebit.co/v1/patients/00000000-0000-4000-8000-000000000004"
  },
  "paid_at": "2026-01-01T09:00:00Z",
  "patient_id": "00000000-0000-4000-8000-000000000004",
  "payment_method_id": "00000000-0000-4000-8000-000000000036",
  "payment_method_type": "bacs_debit",
  "payor_type": "insurance_company",
  "refunds": [
    {
      "id": "d3d1640d-4d73-43b4-81cb-c0dd11bb3a12",
      "object": "refund",
      "amount": 25000,
      "created_at": "2026-01-01T09:00:00Z",
      "currency": "GBP",
      "error": "The destination payment account declined the refund.",
      "invoice_id": "00000000-0000-4000-8000-000000000027",
      "links": {
        "patient": "https://api.carebit.co/v1/patients/00000000-0000-4000-8000-000000000004"
      },
      "next_steps": null,
      "notes": "Please confirm the appointment by email.",
      "patient_id": "00000000-0000-4000-8000-000000000004",
      "payment_id": "00000000-0000-4000-8000-000000000026",
      "reason": "other",
      "refund_source": "bank_account",
      "status": "canceled",
      "succeeded_at": "2026-01-01T09:00:00Z",
      "updated_at": "2026-01-01T09:00:00Z"
    }
  ],
  "status": "awaiting_authentication",
  "updated_at": "2026-01-01T09:00:00Z"
}
```

## Response `400`

The `Idempotency-Key` header is missing (`idempotency_key_required`) or exceeds 255 characters (`idempotency_key_too_long`).

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `401`

The access token is missing, invalid, expired, or revoked.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `403`

The access token lacks the required scope, or the project is disabled.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `404`

The Patient, Invoice, Clinician, or PaymentMethod was not found in the Organization.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `409`

A concurrent request holds the idempotency lease (`idempotency_conflict`). Retry after the delay indicated by `Retry-After`.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `422`

The Payment cannot be created. The Patient must already have an active stored card PaymentMethod that this Organization can access. List PaymentMethods with GET /v1/patients/{id}/payment_methods, or use `patient_portal_add_payment_method_url` on the Patient object to collect one. Other payment method types are not accepted. The Invoice must belong to the Patient and must be able to accept a Payment.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `429`

Too many requests. Retry after the delay indicated by `Retry-After`.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Code samples

```bash
curl -X POST "https://api.carebit.co/v1/payments" \
  -H "Authorization: Bearer $CAREBIT_ACCESS_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "amount": 25000,
  "invoice_id": "00000000-0000-4000-8000-000000000027",
  "patient_id": "00000000-0000-4000-8000-000000000004",
  "payment_method_id": "00000000-0000-4000-8000-000000000036",
  "payment_method_type": "card"
}'
```

```javascript
const response = await fetch("https://api.carebit.co/v1/payments", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.CAREBIT_ACCESS_TOKEN}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({
  "amount": 25000,
  "invoice_id": "00000000-0000-4000-8000-000000000027",
  "patient_id": "00000000-0000-4000-8000-000000000004",
  "payment_method_id": "00000000-0000-4000-8000-000000000036",
  "payment_method_type": "card"
}),
});

if (!response.ok) {
  throw new Error(`Carebit API error: ${response.status}`);
}

const data = await response.json();
```

```python
import os
import requests
import uuid

response = requests.post(
    "https://api.carebit.co/v1/payments",
    headers={
        "Authorization": f"Bearer {os.environ['CAREBIT_ACCESS_TOKEN']}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "amount": 25000,
        "invoice_id": "00000000-0000-4000-8000-000000000027",
        "patient_id": "00000000-0000-4000-8000-000000000004",
        "payment_method_id": "00000000-0000-4000-8000-000000000036",
        "payment_method_type": "card"
    }
)
response.raise_for_status()
data = response.json()
```

```ruby
require "httparty"
require "json"
require "securerandom"

response = HTTParty.post(
  "https://api.carebit.co/v1/payments",
  headers: {
    "Authorization" => "Bearer #{ENV.fetch("CAREBIT_ACCESS_TOKEN")}",
    "Idempotency-Key" => SecureRandom.uuid,
    "Content-Type" => "application/json"
  },
  body: {
    "amount" => 25000,
    "invoice_id" => "00000000-0000-4000-8000-000000000027",
    "patient_id" => "00000000-0000-4000-8000-000000000004",
    "payment_method_id" => "00000000-0000-4000-8000-000000000036",
    "payment_method_type" => "card"
  }.to_json
)
raise "Carebit API error: #{response.code}" unless response.success?
data = response.parsed_response
```

```php
<?php

$client = new GuzzleHttp\Client();

$response = $client->post("https://api.carebit.co/v1/payments", [
    "headers" => [
      "Authorization" => "Bearer " . getenv("CAREBIT_ACCESS_TOKEN"),
      "Idempotency-Key" => bin2hex(random_bytes(16)),
    ],
    "json" => [
      "amount" => 25000,
      "invoice_id" => "00000000-0000-4000-8000-000000000027",
      "patient_id" => "00000000-0000-4000-8000-000000000004",
      "payment_method_id" => "00000000-0000-4000-8000-000000000036",
      "payment_method_type" => "card"
    ]
]);
$data = json_decode((string) $response->getBody(), true, flags: JSON_THROW_ON_ERROR);
```


## Create a Refund

`POST /v1/payments/:id/refunds`

Creates a Refund against a card Payment. Requires `refunds.create`. Amounts are minor currency units. `reason` is required. Carebit returns the funds to the original card through Stripe and the Refund starts as `processing`. If Stripe rejects the Refund, Carebit emits `refund.failed` with the error message and next steps.

**Required API scopes:** `refunds.create`

## Parameters

- `id` (path, `string`) (required)
- `Idempotency-Key` (header, `string`) (required) - Client-generated idempotency key. Required for every POST/PATCH write. Replay of the same key with the same body returns the stored response with an `Idempotency-Replayed: true` header. Same key + different body returns `422 idempotency_key_reused`. A duplicate that arrives while the first request is still in flight returns `409 idempotency_conflict` with `Retry-After: 1`.

## Request body (`application/json`)

- `object`
  - `amount` (`integer`) - The amount to refund in minor currency units. Must not exceed the Payment's remaining refundable amount.
  - `notes` (`string | null`) - Internal notes recorded with the Refund.
  - `notify_payor` (`boolean`) - Whether to notify the payor when the Refund succeeds. Defaults to `true`.
  - `reason` (`string`) - enum: `other`, `other_party_will_pay`, `overcharged`, `overpaid`, `service_not_delivered`; The reason the Refund is being issued.

### Example

```json
{
  "amount": 25000,
  "notes": "Refund for a canceled consultation.",
  "reason": "service_not_delivered"
}
```

## Response `201`

The requested `Refund`.

- `object`
  - `amount` (`integer`) - The refunded amount in minor currency units.
  - `created_at` (`string`) - format: `date-time`; An ISO 8601 timestamp in UTC, with a `Z` suffix. For example, `2026-07-01T09:00:00Z`.
  - `currency` (`string | null`) - The ISO 4217 currency code.
  - `error` (`string | null`) - The failure message when the Refund did not complete.
  - `id` (`string`) - format: `uuid`; The resource's unique identifier, formatted as an RFC 4122 version 4 UUID.
  - `invoice_id` (`string | null`) - format: `uuid`; The Invoice associated with the refunded Payment, when one is assigned.
  - `links` (`object`) - URLs to related resources.
    - `patient` (`string | null`) - format: `uri`; The full URL of a related resource.
  - `next_steps` (`string | null`) - Guidance for completing the Refund after a Stripe refund to the original payment method fails. Null unless `status` is `failed` and `refund_source` is `payment_account`.
  - `notes` (`string | null`) - Internal notes recorded with the Refund.
  - `object` (`any`) - Discriminator value emitted at `object`.
  - `patient_id` (`string | null`) - format: `uuid`; The Patient associated with the Refund.
  - `payment_id` (`string | null`) - format: `uuid`; The Payment this Refund was issued against.
  - `reason` (`string | null`) - enum: `other`, `other_party_will_pay`, `overcharged`, `overpaid`, `service_not_delivered`, `null`; The reason the Refund was issued.
  - `refund_source` (`string | null`) - enum: `bank_account`, `deduction_from_balance`, `payment_account`, `null`; How the Refund is paid.
  - `status` (`string | null`) - enum: `canceled`, `failed`, `processing`, `requires_action`, `succeeded`, `null`; The Refund's current status.
  - `succeeded_at` (`string | null`) - format: `date-time`; An ISO 8601 timestamp in UTC, with a `Z` suffix. For example, `2026-07-01T09:00:00Z`.
  - `updated_at` (`string`) - format: `date-time`; An ISO 8601 timestamp in UTC, with a `Z` suffix. For example, `2026-07-01T09:00:00Z`.

### Example

```json
{
  "id": "d3d1640d-4d73-43b4-81cb-c0dd11bb3a12",
  "object": "refund",
  "amount": 25000,
  "created_at": "2026-01-01T09:00:00Z",
  "currency": "GBP",
  "error": "The destination payment account declined the refund.",
  "invoice_id": "00000000-0000-4000-8000-000000000027",
  "links": {
    "patient": "https://api.carebit.co/v1/patients/00000000-0000-4000-8000-000000000004"
  },
  "next_steps": null,
  "notes": "Please confirm the appointment by email.",
  "patient_id": "00000000-0000-4000-8000-000000000004",
  "payment_id": "00000000-0000-4000-8000-000000000026",
  "reason": "other",
  "refund_source": "bank_account",
  "status": "canceled",
  "succeeded_at": "2026-01-01T09:00:00Z",
  "updated_at": "2026-01-01T09:00:00Z"
}
```

## Response `400`

The `Idempotency-Key` header is missing (`idempotency_key_required`) or exceeds 255 characters (`idempotency_key_too_long`).

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `401`

The access token is missing, invalid, expired, or revoked.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `403`

The access token lacks the required scope, or the project is disabled.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `404`

The Payment was not found in the Organization.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `409`

A concurrent request holds the idempotency lease (`idempotency_conflict`). Retry after the delay indicated by `Retry-After`.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `422`

The Refund cannot be created. The Payment must be a `paid` or `partially_refunded` card Payment, and `amount` must not exceed the remaining refundable amount.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `429`

Too many requests. Retry after the delay indicated by `Retry-After`.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Code samples

```bash
curl -X POST "https://api.carebit.co/v1/payments/00000000-0000-4000-8000-000000000026/refunds" \
  -H "Authorization: Bearer $CAREBIT_ACCESS_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "amount": 25000,
  "notes": "Refund for a canceled consultation.",
  "reason": "service_not_delivered"
}'
```

```javascript
const response = await fetch("https://api.carebit.co/v1/payments/00000000-0000-4000-8000-000000000026/refunds", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.CAREBIT_ACCESS_TOKEN}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({
  "amount": 25000,
  "notes": "Refund for a canceled consultation.",
  "reason": "service_not_delivered"
}),
});

if (!response.ok) {
  throw new Error(`Carebit API error: ${response.status}`);
}

const data = await response.json();
```

```python
import os
import requests
import uuid

response = requests.post(
    "https://api.carebit.co/v1/payments/00000000-0000-4000-8000-000000000026/refunds",
    headers={
        "Authorization": f"Bearer {os.environ['CAREBIT_ACCESS_TOKEN']}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "amount": 25000,
        "notes": "Refund for a canceled consultation.",
        "reason": "service_not_delivered"
    }
)
response.raise_for_status()
data = response.json()
```

```ruby
require "httparty"
require "json"
require "securerandom"

response = HTTParty.post(
  "https://api.carebit.co/v1/payments/00000000-0000-4000-8000-000000000026/refunds",
  headers: {
    "Authorization" => "Bearer #{ENV.fetch("CAREBIT_ACCESS_TOKEN")}",
    "Idempotency-Key" => SecureRandom.uuid,
    "Content-Type" => "application/json"
  },
  body: {
    "amount" => 25000,
    "notes" => "Refund for a canceled consultation.",
    "reason" => "service_not_delivered"
  }.to_json
)
raise "Carebit API error: #{response.code}" unless response.success?
data = response.parsed_response
```

```php
<?php

$client = new GuzzleHttp\Client();

$response = $client->post("https://api.carebit.co/v1/payments/00000000-0000-4000-8000-000000000026/refunds", [
    "headers" => [
      "Authorization" => "Bearer " . getenv("CAREBIT_ACCESS_TOKEN"),
      "Idempotency-Key" => bin2hex(random_bytes(16)),
    ],
    "json" => [
      "amount" => 25000,
      "notes" => "Refund for a canceled consultation.",
      "reason" => "service_not_delivered"
    ]
]);
$data = json_decode((string) $response->getBody(), true, flags: JSON_THROW_ON_ERROR);
```

