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

# customerPayments

> Project-isolated payments for a customer's end users. This is separate from Korve organization billing; processor credentials are write-only.

Project-isolated payments for a customer's end users. This is separate from Korve organization billing; processor credentials are write-only.

## customerPayments.getConfig

Get payment readiness and webhook configuration without returning secrets.

```text theme={null}
GET /v1/orgs/{orgId}/projects/{projectId}/payments
```

* **Auth:** Organization API key (`Authorization: Bearer korve_...`) or dashboard session.
* **Minimum role:** `member`
* **Risk:** `read`

### Query parameters

| Name          | Type   | Required | Description                        |
| ------------- | ------ | -------- | ---------------------------------- |
| `environment` | string | No       | Pattern: `^[a-z][a-z0-9-]{0,38}$`. |

### Example

```bash theme={null}
curl "https://api.korve.dev/v1/orgs/$ORG_ID/projects/$PROJECT_ID/payments?environment=example" \
  -H "Authorization: Bearer korve_..."
```

### Responses

| Status | Description                                              |
| ------ | -------------------------------------------------------- |
| `200`  | Payment configuration.                                   |
| `402`  | Organization billing is not active.                      |
| `404`  | Project or payment resource not found.                   |
| `409`  | Duplicate idempotency key or conflicting resource state. |
| `422`  | Invalid payment request.                                 |
| `502`  | The payment processor rejected the request.              |
| `503`  | The payment processor is temporarily unavailable.        |

### Response body (200)

| Field                     | Type           | Description                                                                                                      |
| ------------------------- | -------------- | ---------------------------------------------------------------------------------------------------------------- |
| `status`                  | string         | One of: `not_configured`, `ready`, `disabled`.                                                                   |
| `credentialConfigured`    | boolean        | —                                                                                                                |
| `credentialHint`          | string or null | —                                                                                                                |
| `webhookUrl`              | string or null | Format: `uri`.                                                                                                   |
| `webhookSecretConfigured` | boolean        | —                                                                                                                |
| `credentialScopes`        | string\[]      | Korve-side operation allowlist. This does not claim or replace the connected processor's credential permissions. |
| `allowedRedirectOrigins`  | string\[]      | —                                                                                                                |
| `connectedAt`             | string or null | Format: `date-time`.                                                                                             |
| `updatedAt`               | string or null | Format: `date-time`.                                                                                             |

## customerPayments.connect

Connect a customer-owned payment account with a write-only restricted credential. Korve rejects full-access credentials, validates authenticated resource access, and enforces the declared Korve operation allowlist; the connected processor remains authoritative for credential permissions.

```text theme={null}
PUT /v1/orgs/{orgId}/projects/{projectId}/payments
```

* **Auth:** Dashboard session only — organization API keys are rejected.
* **Minimum role:** `owner`
* **Billing:** requires active organization billing — returns `402` otherwise.
* **Risk:** `write` · sensitive (elevated blast radius)

### Query parameters

| Name          | Type   | Required | Description                        |
| ------------- | ------ | -------- | ---------------------------------- |
| `environment` | string | No       | Pattern: `^[a-z][a-z0-9-]{0,38}$`. |

### Request body

| Field                    | Type      | Required | Description                                                                                                          |
| ------------------------ | --------- | -------- | -------------------------------------------------------------------------------------------------------------------- |
| `secretKey`              | string    | Yes      | —                                                                                                                    |
| `webhookSecret`          | string    | Yes      | —                                                                                                                    |
| `credentialScopes`       | string\[] | Yes      | Exact Korve operation allowlist. These values constrain Korve and are not self-attested processor permission claims. |
| `allowedRedirectOrigins` | string\[] | Yes      | —                                                                                                                    |

### Example

```bash theme={null}
curl -X PUT "https://api.korve.dev/v1/orgs/$ORG_ID/projects/$PROJECT_ID/payments?environment=example" \
  -b "$SESSION" \
  -H "Content-Type: application/json" \
  -d '{
  "secretKey": "examplexxxxxxxxx",
  "webhookSecret": "examplexxxxxxxxx",
  "credentialScopes": [
    "customers:write",
    "products:write",
    "prices:write",
    "checkout_sessions:write",
    "subscriptions:write"
  ],
  "allowedRedirectOrigins": [
    "https://app.acme.com/callback"
  ]
}'
```

### Responses

| Status | Description                                              |
| ------ | -------------------------------------------------------- |
| `200`  | Connected configuration.                                 |
| `402`  | Organization billing is not active.                      |
| `404`  | Project or payment resource not found.                   |
| `409`  | Duplicate idempotency key or conflicting resource state. |
| `422`  | Invalid payment request.                                 |
| `502`  | The payment processor rejected the request.              |
| `503`  | The payment processor is temporarily unavailable.        |

### Response body (200)

| Field                     | Type           | Description                                                                                                      |
| ------------------------- | -------------- | ---------------------------------------------------------------------------------------------------------------- |
| `status`                  | string         | One of: `not_configured`, `ready`, `disabled`.                                                                   |
| `credentialConfigured`    | boolean        | —                                                                                                                |
| `credentialHint`          | string or null | —                                                                                                                |
| `webhookUrl`              | string or null | Format: `uri`.                                                                                                   |
| `webhookSecretConfigured` | boolean        | —                                                                                                                |
| `credentialScopes`        | string\[]      | Korve-side operation allowlist. This does not claim or replace the connected processor's credential permissions. |
| `allowedRedirectOrigins`  | string\[]      | —                                                                                                                |
| `connectedAt`             | string or null | Format: `date-time`.                                                                                             |
| `updatedAt`               | string or null | Format: `date-time`.                                                                                             |

## customerPayments.disconnect

Revoke the local connection and permanently delete payment projections and audit events.

```text theme={null}
DELETE /v1/orgs/{orgId}/projects/{projectId}/payments
```

* **Auth:** Dashboard session only — organization API keys are rejected.
* **Minimum role:** `owner`
* **Risk:** `destructive` · sensitive (elevated blast radius)

### Query parameters

| Name          | Type   | Required | Description                        |
| ------------- | ------ | -------- | ---------------------------------- |
| `environment` | string | No       | Pattern: `^[a-z][a-z0-9-]{0,38}$`. |

### Example

```bash theme={null}
curl -X DELETE "https://api.korve.dev/v1/orgs/$ORG_ID/projects/$PROJECT_ID/payments?environment=example" \
  -b "$SESSION"
```

### Responses

| Status | Description                                              |
| ------ | -------------------------------------------------------- |
| `200`  | Payments disconnected.                                   |
| `402`  | Organization billing is not active.                      |
| `404`  | Project or payment resource not found.                   |
| `409`  | Duplicate idempotency key or conflicting resource state. |
| `422`  | Invalid payment request.                                 |
| `502`  | The payment processor rejected the request.              |
| `503`  | The payment processor is temporarily unavailable.        |

## customerPayments.listProducts

List products in ascending creation order, with UUID as the stable tie-breaker.

```text theme={null}
GET /v1/orgs/{orgId}/projects/{projectId}/payments/products
```

* **Auth:** Organization API key (`Authorization: Bearer korve_...`) or dashboard session.
* **Minimum role:** `member`
* **Risk:** `read`

### Query parameters

| Name          | Type    | Required | Description                        |
| ------------- | ------- | -------- | ---------------------------------- |
| `environment` | string  | No       | Pattern: `^[a-z][a-z0-9-]{0,38}$`. |
| `cursor`      | string  | No       | —                                  |
| `limit`       | integer | No       | —                                  |

### Example

```bash theme={null}
curl "https://api.korve.dev/v1/orgs/$ORG_ID/projects/$PROJECT_ID/payments/products?environment=example" \
  -H "Authorization: Bearer korve_..."
```

### Responses

| Status | Description                                              |
| ------ | -------------------------------------------------------- |
| `200`  | Product page.                                            |
| `402`  | Organization billing is not active.                      |
| `404`  | Project or payment resource not found.                   |
| `409`  | Duplicate idempotency key or conflicting resource state. |
| `422`  | Invalid payment request.                                 |
| `502`  | The payment processor rejected the request.              |
| `503`  | The payment processor is temporarily unavailable.        |

### Response body (200)

| Field                 | Type           | Description                                                          |
| --------------------- | -------------- | -------------------------------------------------------------------- |
| `items`               | object\[]      | —                                                                    |
| `items[].id`          | string         | Format: `uuid`.                                                      |
| `items[].name`        | string         | —                                                                    |
| `items[].description` | string or null | —                                                                    |
| `items[].active`      | boolean        | —                                                                    |
| `items[].metadata`    | object         | At most 20 application-defined string entries. Map of string values. |
| `items[].createdAt`   | string         | Format: `date-time`.                                                 |
| `items[].updatedAt`   | string         | Format: `date-time`.                                                 |
| `nextCursor`          | string or null | —                                                                    |

## customerPayments.createProduct

Create a product idempotently.

```text theme={null}
POST /v1/orgs/{orgId}/projects/{projectId}/payments/products
```

* **Auth:** Organization API key (`Authorization: Bearer korve_...`) or dashboard session.
* **Minimum role:** `admin`
* **Billing:** requires active organization billing — returns `402` otherwise.
* **Risk:** `write`

### Query parameters

| Name          | Type   | Required | Description                        |
| ------------- | ------ | -------- | ---------------------------------- |
| `environment` | string | No       | Pattern: `^[a-z][a-z0-9-]{0,38}$`. |

### Request body

| Field         | Type   | Required | Description                                                          |
| ------------- | ------ | -------- | -------------------------------------------------------------------- |
| `name`        | string | Yes      | —                                                                    |
| `description` | string | No       | —                                                                    |
| `metadata`    | object | No       | At most 20 application-defined string entries. Map of string values. |

### Example

```bash theme={null}
curl -X POST "https://api.korve.dev/v1/orgs/$ORG_ID/projects/$PROJECT_ID/payments/products?environment=example" \
  -H "Authorization: Bearer korve_..." \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Storefront"
}'
```

### Responses

| Status | Description                                              |
| ------ | -------------------------------------------------------- |
| `201`  | Created product.                                         |
| `402`  | Organization billing is not active.                      |
| `404`  | Project or payment resource not found.                   |
| `409`  | Duplicate idempotency key or conflicting resource state. |
| `422`  | Invalid payment request.                                 |
| `502`  | The payment processor rejected the request.              |
| `503`  | The payment processor is temporarily unavailable.        |

### Response body (201)

| Field         | Type           | Description                                                          |
| ------------- | -------------- | -------------------------------------------------------------------- |
| `id`          | string         | Format: `uuid`.                                                      |
| `name`        | string         | —                                                                    |
| `description` | string or null | —                                                                    |
| `active`      | boolean        | —                                                                    |
| `metadata`    | object         | At most 20 application-defined string entries. Map of string values. |
| `createdAt`   | string         | Format: `date-time`.                                                 |
| `updatedAt`   | string         | Format: `date-time`.                                                 |

## customerPayments.updateProduct

Update or archive a product idempotently.

```text theme={null}
PATCH /v1/orgs/{orgId}/projects/{projectId}/payments/products/{productId}
```

* **Auth:** Organization API key (`Authorization: Bearer korve_...`) or dashboard session.
* **Minimum role:** `admin`
* **Risk:** `write`

### Query parameters

| Name          | Type   | Required | Description                        |
| ------------- | ------ | -------- | ---------------------------------- |
| `environment` | string | No       | Pattern: `^[a-z][a-z0-9-]{0,38}$`. |

### Request body

| Field         | Type    | Required | Description                                                          |
| ------------- | ------- | -------- | -------------------------------------------------------------------- |
| `name`        | string  | Yes      | —                                                                    |
| `description` | string  | No       | —                                                                    |
| `metadata`    | object  | No       | At most 20 application-defined string entries. Map of string values. |
| `active`      | boolean | No       | —                                                                    |

### Example

```bash theme={null}
curl -X PATCH "https://api.korve.dev/v1/orgs/$ORG_ID/projects/$PROJECT_ID/payments/products/$PRODUCT_ID?environment=example" \
  -H "Authorization: Bearer korve_..." \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Storefront"
}'
```

### Responses

| Status | Description                                              |
| ------ | -------------------------------------------------------- |
| `200`  | Updated product.                                         |
| `402`  | Organization billing is not active.                      |
| `404`  | Project or payment resource not found.                   |
| `409`  | Duplicate idempotency key or conflicting resource state. |
| `422`  | Invalid payment request.                                 |
| `502`  | The payment processor rejected the request.              |
| `503`  | The payment processor is temporarily unavailable.        |

### Response body (200)

| Field         | Type           | Description                                                          |
| ------------- | -------------- | -------------------------------------------------------------------- |
| `id`          | string         | Format: `uuid`.                                                      |
| `name`        | string         | —                                                                    |
| `description` | string or null | —                                                                    |
| `active`      | boolean        | —                                                                    |
| `metadata`    | object         | At most 20 application-defined string entries. Map of string values. |
| `createdAt`   | string         | Format: `date-time`.                                                 |
| `updatedAt`   | string         | Format: `date-time`.                                                 |

## customerPayments.listPrices

List immutable prices in ascending creation order, with UUID as the stable tie-breaker.

```text theme={null}
GET /v1/orgs/{orgId}/projects/{projectId}/payments/prices
```

* **Auth:** Organization API key (`Authorization: Bearer korve_...`) or dashboard session.
* **Minimum role:** `member`
* **Risk:** `read`

### Query parameters

| Name          | Type    | Required | Description                        |
| ------------- | ------- | -------- | ---------------------------------- |
| `environment` | string  | No       | Pattern: `^[a-z][a-z0-9-]{0,38}$`. |
| `cursor`      | string  | No       | —                                  |
| `limit`       | integer | No       | —                                  |

### Example

```bash theme={null}
curl "https://api.korve.dev/v1/orgs/$ORG_ID/projects/$PROJECT_ID/payments/prices?environment=example" \
  -H "Authorization: Bearer korve_..."
```

### Responses

| Status | Description                                              |
| ------ | -------------------------------------------------------- |
| `200`  | Price page.                                              |
| `402`  | Organization billing is not active.                      |
| `404`  | Project or payment resource not found.                   |
| `409`  | Duplicate idempotency key or conflicting resource state. |
| `422`  | Invalid payment request.                                 |
| `502`  | The payment processor rejected the request.              |
| `503`  | The payment processor is temporarily unavailable.        |

### Response body (200)

| Field                   | Type            | Description                                                          |
| ----------------------- | --------------- | -------------------------------------------------------------------- |
| `items`                 | object\[]       | —                                                                    |
| `items[].id`            | string          | Format: `uuid`.                                                      |
| `items[].productId`     | string          | Format: `uuid`.                                                      |
| `items[].currency`      | string          | Pattern: `^[a-z]{3}$`.                                               |
| `items[].unitAmount`    | integer         | —                                                                    |
| `items[].mode`          | string          | One of: `one_time`, `subscription`.                                  |
| `items[].interval`      | string or null  | One of: `day`, `week`, `month`, `year`, `null`.                      |
| `items[].intervalCount` | integer or null | —                                                                    |
| `items[].active`        | boolean         | —                                                                    |
| `items[].metadata`      | object          | At most 20 application-defined string entries. Map of string values. |
| `items[].createdAt`     | string          | Format: `date-time`.                                                 |
| `nextCursor`            | string or null  | —                                                                    |

## customerPayments.createPrice

Create an immutable one-time or recurring price idempotently.

```text theme={null}
POST /v1/orgs/{orgId}/projects/{projectId}/payments/prices
```

* **Auth:** Organization API key (`Authorization: Bearer korve_...`) or dashboard session.
* **Minimum role:** `admin`
* **Billing:** requires active organization billing — returns `402` otherwise.
* **Risk:** `write`

### Query parameters

| Name          | Type   | Required | Description                        |
| ------------- | ------ | -------- | ---------------------------------- |
| `environment` | string | No       | Pattern: `^[a-z][a-z0-9-]{0,38}$`. |

### Request body

| Field           | Type    | Required | Description                                                          |
| --------------- | ------- | -------- | -------------------------------------------------------------------- |
| `productId`     | string  | Yes      | Format: `uuid`.                                                      |
| `currency`      | string  | Yes      | Pattern: `^[A-Za-z]{3}$`.                                            |
| `unitAmount`    | integer | Yes      | —                                                                    |
| `mode`          | string  | Yes      | One of: `one_time`, `subscription`.                                  |
| `interval`      | string  | No       | One of: `day`, `week`, `month`, `year`.                              |
| `intervalCount` | integer | No       | —                                                                    |
| `metadata`      | object  | No       | At most 20 application-defined string entries. Map of string values. |

### Example

```bash theme={null}
curl -X POST "https://api.korve.dev/v1/orgs/$ORG_ID/projects/$PROJECT_ID/payments/prices?environment=example" \
  -H "Authorization: Bearer korve_..." \
  -H "Content-Type: application/json" \
  -d '{
  "productId": "00000000-0000-4000-8000-000000000001",
  "currency": "USD",
  "unitAmount": 1,
  "mode": "one_time"
}'
```

### Responses

| Status | Description                                              |
| ------ | -------------------------------------------------------- |
| `201`  | Created price.                                           |
| `402`  | Organization billing is not active.                      |
| `404`  | Project or payment resource not found.                   |
| `409`  | Duplicate idempotency key or conflicting resource state. |
| `422`  | Invalid payment request.                                 |
| `502`  | The payment processor rejected the request.              |
| `503`  | The payment processor is temporarily unavailable.        |

### Response body (201)

| Field           | Type            | Description                                                          |
| --------------- | --------------- | -------------------------------------------------------------------- |
| `id`            | string          | Format: `uuid`.                                                      |
| `productId`     | string          | Format: `uuid`.                                                      |
| `currency`      | string          | Pattern: `^[a-z]{3}$`.                                               |
| `unitAmount`    | integer         | —                                                                    |
| `mode`          | string          | One of: `one_time`, `subscription`.                                  |
| `interval`      | string or null  | One of: `day`, `week`, `month`, `year`, `null`.                      |
| `intervalCount` | integer or null | —                                                                    |
| `active`        | boolean         | —                                                                    |
| `metadata`      | object          | At most 20 application-defined string entries. Map of string values. |
| `createdAt`     | string          | Format: `date-time`.                                                 |

## customerPayments.archivePrice

Archive an immutable price. Existing subscriptions remain valid.

```text theme={null}
DELETE /v1/orgs/{orgId}/projects/{projectId}/payments/prices/{priceId}
```

* **Auth:** Organization API key (`Authorization: Bearer korve_...`) or dashboard session.
* **Minimum role:** `admin`
* **Risk:** `destructive` · sensitive (elevated blast radius)

### Query parameters

| Name          | Type   | Required | Description                        |
| ------------- | ------ | -------- | ---------------------------------- |
| `environment` | string | No       | Pattern: `^[a-z][a-z0-9-]{0,38}$`. |

### Example

```bash theme={null}
curl -X DELETE "https://api.korve.dev/v1/orgs/$ORG_ID/projects/$PROJECT_ID/payments/prices/$PRICE_ID?environment=example" \
  -H "Authorization: Bearer korve_..."
```

### Responses

| Status | Description                                              |
| ------ | -------------------------------------------------------- |
| `200`  | Archived price.                                          |
| `402`  | Organization billing is not active.                      |
| `404`  | Project or payment resource not found.                   |
| `409`  | Duplicate idempotency key or conflicting resource state. |
| `422`  | Invalid payment request.                                 |
| `502`  | The payment processor rejected the request.              |
| `503`  | The payment processor is temporarily unavailable.        |

### Response body (200)

| Field           | Type            | Description                                                          |
| --------------- | --------------- | -------------------------------------------------------------------- |
| `id`            | string          | Format: `uuid`.                                                      |
| `productId`     | string          | Format: `uuid`.                                                      |
| `currency`      | string          | Pattern: `^[a-z]{3}$`.                                               |
| `unitAmount`    | integer         | —                                                                    |
| `mode`          | string          | One of: `one_time`, `subscription`.                                  |
| `interval`      | string or null  | One of: `day`, `week`, `month`, `year`, `null`.                      |
| `intervalCount` | integer or null | —                                                                    |
| `active`        | boolean         | —                                                                    |
| `metadata`      | object          | At most 20 application-defined string entries. Map of string values. |
| `createdAt`     | string          | Format: `date-time`.                                                 |

## customerPayments.listCustomers

List end-customer projections in ascending creation order, with UUID as the stable tie-breaker.

```text theme={null}
GET /v1/orgs/{orgId}/projects/{projectId}/payments/customers
```

* **Auth:** Organization API key (`Authorization: Bearer korve_...`) or dashboard session.
* **Minimum role:** `admin`
* **Risk:** `read`

### Query parameters

| Name          | Type    | Required | Description                        |
| ------------- | ------- | -------- | ---------------------------------- |
| `environment` | string  | No       | Pattern: `^[a-z][a-z0-9-]{0,38}$`. |
| `cursor`      | string  | No       | —                                  |
| `limit`       | integer | No       | —                                  |

### Example

```bash theme={null}
curl "https://api.korve.dev/v1/orgs/$ORG_ID/projects/$PROJECT_ID/payments/customers?environment=example" \
  -H "Authorization: Bearer korve_..."
```

### Responses

| Status | Description                                              |
| ------ | -------------------------------------------------------- |
| `200`  | Customer page.                                           |
| `402`  | Organization billing is not active.                      |
| `404`  | Project or payment resource not found.                   |
| `409`  | Duplicate idempotency key or conflicting resource state. |
| `422`  | Invalid payment request.                                 |
| `502`  | The payment processor rejected the request.              |
| `503`  | The payment processor is temporarily unavailable.        |

### Response body (200)

| Field                | Type           | Description                                                          |
| -------------------- | -------------- | -------------------------------------------------------------------- |
| `items`              | object\[]      | —                                                                    |
| `items[].id`         | string         | Format: `uuid`.                                                      |
| `items[].email`      | string         | Format: `email`.                                                     |
| `items[].name`       | string or null | —                                                                    |
| `items[].externalId` | string or null | —                                                                    |
| `items[].metadata`   | object         | At most 20 application-defined string entries. Map of string values. |
| `items[].createdAt`  | string         | Format: `date-time`.                                                 |
| `nextCursor`         | string or null | —                                                                    |

## customerPayments.createCustomer

Create an end customer idempotently.

```text theme={null}
POST /v1/orgs/{orgId}/projects/{projectId}/payments/customers
```

* **Auth:** Organization API key (`Authorization: Bearer korve_...`) or dashboard session.
* **Minimum role:** `admin`
* **Billing:** requires active organization billing — returns `402` otherwise.
* **Risk:** `write`

### Query parameters

| Name          | Type   | Required | Description                        |
| ------------- | ------ | -------- | ---------------------------------- |
| `environment` | string | No       | Pattern: `^[a-z][a-z0-9-]{0,38}$`. |

### Request body

| Field        | Type   | Required | Description                                                          |
| ------------ | ------ | -------- | -------------------------------------------------------------------- |
| `email`      | string | Yes      | Format: `email`.                                                     |
| `name`       | string | No       | —                                                                    |
| `externalId` | string | No       | —                                                                    |
| `metadata`   | object | No       | At most 20 application-defined string entries. Map of string values. |

### Example

```bash theme={null}
curl -X POST "https://api.korve.dev/v1/orgs/$ORG_ID/projects/$PROJECT_ID/payments/customers?environment=example" \
  -H "Authorization: Bearer korve_..." \
  -H "Content-Type: application/json" \
  -d '{
  "email": "dev@acme.com"
}'
```

### Responses

| Status | Description                                              |
| ------ | -------------------------------------------------------- |
| `201`  | Created customer.                                        |
| `402`  | Organization billing is not active.                      |
| `404`  | Project or payment resource not found.                   |
| `409`  | Duplicate idempotency key or conflicting resource state. |
| `422`  | Invalid payment request.                                 |
| `502`  | The payment processor rejected the request.              |
| `503`  | The payment processor is temporarily unavailable.        |

### Response body (201)

| Field        | Type           | Description                                                          |
| ------------ | -------------- | -------------------------------------------------------------------- |
| `id`         | string         | Format: `uuid`.                                                      |
| `email`      | string         | Format: `email`.                                                     |
| `name`       | string or null | —                                                                    |
| `externalId` | string or null | —                                                                    |
| `metadata`   | object         | At most 20 application-defined string entries. Map of string values. |
| `createdAt`  | string         | Format: `date-time`.                                                 |

## customerPayments.eraseCustomer

Erase end-customer personal data at the processor and redact the local projection.

```text theme={null}
DELETE /v1/orgs/{orgId}/projects/{projectId}/payments/customers/{customerId}
```

* **Auth:** Organization API key (`Authorization: Bearer korve_...`) or dashboard session.
* **Minimum role:** `admin`
* **Risk:** `destructive` · sensitive (elevated blast radius)

### Query parameters

| Name          | Type   | Required | Description                        |
| ------------- | ------ | -------- | ---------------------------------- |
| `environment` | string | No       | Pattern: `^[a-z][a-z0-9-]{0,38}$`. |

### Example

```bash theme={null}
curl -X DELETE "https://api.korve.dev/v1/orgs/$ORG_ID/projects/$PROJECT_ID/payments/customers/$CUSTOMER_ID?environment=example" \
  -H "Authorization: Bearer korve_..."
```

### Responses

| Status | Description                                              |
| ------ | -------------------------------------------------------- |
| `200`  | Customer erased.                                         |
| `402`  | Organization billing is not active.                      |
| `404`  | Project or payment resource not found.                   |
| `409`  | Duplicate idempotency key or conflicting resource state. |
| `422`  | Invalid payment request.                                 |
| `502`  | The payment processor rejected the request.              |
| `503`  | The payment processor is temporarily unavailable.        |

## customerPayments.runtimeCreateCustomer

Create an end customer from the deployed server runtime scoped to exactly one project and environment.

```text theme={null}
POST /v1/runtime/payments/customers
```

* **Auth:** Project app-service token (`Authorization: Bearer $APP_SERVICE_TOKEN`).
* **Billing:** requires active organization billing — returns `402` otherwise.
* **Risk:** `write`

### Request body

| Field        | Type   | Required | Description                                                          |
| ------------ | ------ | -------- | -------------------------------------------------------------------- |
| `email`      | string | Yes      | Format: `email`.                                                     |
| `name`       | string | No       | —                                                                    |
| `externalId` | string | No       | —                                                                    |
| `metadata`   | object | No       | At most 20 application-defined string entries. Map of string values. |

### Example

```bash theme={null}
curl -X POST "https://api.korve.dev/v1/runtime/payments/customers" \
  -H "Authorization: Bearer $APP_SERVICE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "email": "dev@acme.com"
}'
```

### Responses

| Status | Description                                              |
| ------ | -------------------------------------------------------- |
| `201`  | Created customer.                                        |
| `402`  | Organization billing is not active.                      |
| `404`  | Project or payment resource not found.                   |
| `409`  | Duplicate idempotency key or conflicting resource state. |
| `422`  | Invalid payment request.                                 |
| `502`  | The payment processor rejected the request.              |
| `503`  | The payment processor is temporarily unavailable.        |

### Response body (201)

| Field        | Type           | Description                                                          |
| ------------ | -------------- | -------------------------------------------------------------------- |
| `id`         | string         | Format: `uuid`.                                                      |
| `email`      | string         | Format: `email`.                                                     |
| `name`       | string or null | —                                                                    |
| `externalId` | string or null | —                                                                    |
| `metadata`   | object         | At most 20 application-defined string entries. Map of string values. |
| `createdAt`  | string         | Format: `date-time`.                                                 |

## customerPayments.listCheckouts

List checkout sessions in ascending creation order, with UUID as the stable tie-breaker. Hosted URLs are not retained or returned.

```text theme={null}
GET /v1/orgs/{orgId}/projects/{projectId}/payments/checkouts
```

* **Auth:** Organization API key (`Authorization: Bearer korve_...`) or dashboard session.
* **Minimum role:** `member`
* **Risk:** `read`

### Query parameters

| Name          | Type    | Required | Description                        |
| ------------- | ------- | -------- | ---------------------------------- |
| `environment` | string  | No       | Pattern: `^[a-z][a-z0-9-]{0,38}$`. |
| `cursor`      | string  | No       | —                                  |
| `limit`       | integer | No       | —                                  |

### Example

```bash theme={null}
curl "https://api.korve.dev/v1/orgs/$ORG_ID/projects/$PROJECT_ID/payments/checkouts?environment=example" \
  -H "Authorization: Bearer korve_..."
```

### Responses

| Status | Description                                              |
| ------ | -------------------------------------------------------- |
| `200`  | Checkout page.                                           |
| `402`  | Organization billing is not active.                      |
| `404`  | Project or payment resource not found.                   |
| `409`  | Duplicate idempotency key or conflicting resource state. |
| `422`  | Invalid payment request.                                 |
| `502`  | The payment processor rejected the request.              |
| `503`  | The payment processor is temporarily unavailable.        |

### Response body (200)

| Field                       | Type           | Description                                                                        |
| --------------------------- | -------------- | ---------------------------------------------------------------------------------- |
| `items`                     | object\[]      | —                                                                                  |
| `items[].id`                | string         | Format: `uuid`.                                                                    |
| `items[].priceId`           | string         | Format: `uuid`.                                                                    |
| `items[].customerId`        | string or null | Format: `uuid`.                                                                    |
| `items[].mode`              | string         | One of: `one_time`, `subscription`.                                                |
| `items[].quantity`          | integer        | —                                                                                  |
| `items[].status`            | string         | One of: `open`, `complete`, `expired`.                                             |
| `items[].paymentStatus`     | string         | One of: `unpaid`, `paid`, `no_payment_required`, `refunded`, `partially_refunded`. |
| `items[].settlementStatus`  | string         | One of: `pending`, `available`, `paid`, `failed`, `not_applicable`.                |
| `items[].url`               | string or null | Hosted checkout URL. Present only on the create response. Format: `uri`.           |
| `items[].clientReferenceId` | string or null | —                                                                                  |
| `items[].expiresAt`         | string         | Format: `date-time`.                                                               |
| `items[].createdAt`         | string         | Format: `date-time`.                                                               |
| `nextCursor`                | string or null | —                                                                                  |

## customerPayments.createCheckout

Create a hosted checkout session idempotently.

```text theme={null}
POST /v1/orgs/{orgId}/projects/{projectId}/payments/checkouts
```

* **Auth:** Organization API key (`Authorization: Bearer korve_...`) or dashboard session.
* **Minimum role:** `admin`
* **Billing:** requires active organization billing — returns `402` otherwise.
* **Risk:** `write` · sensitive (elevated blast radius)

### Query parameters

| Name          | Type   | Required | Description                        |
| ------------- | ------ | -------- | ---------------------------------- |
| `environment` | string | No       | Pattern: `^[a-z][a-z0-9-]{0,38}$`. |

### Request body

| Field               | Type    | Required | Description                                                          |
| ------------------- | ------- | -------- | -------------------------------------------------------------------- |
| `priceId`           | string  | Yes      | Format: `uuid`.                                                      |
| `quantity`          | integer | No       | —                                                                    |
| `successUrl`        | string  | Yes      | Format: `uri`.                                                       |
| `cancelUrl`         | string  | Yes      | Format: `uri`.                                                       |
| `customerId`        | string  | No       | Format: `uuid`.                                                      |
| `customerEmail`     | string  | No       | Format: `email`.                                                     |
| `clientReferenceId` | string  | No       | —                                                                    |
| `metadata`          | object  | No       | At most 20 application-defined string entries. Map of string values. |

### Example

```bash theme={null}
curl -X POST "https://api.korve.dev/v1/orgs/$ORG_ID/projects/$PROJECT_ID/payments/checkouts?environment=example" \
  -H "Authorization: Bearer korve_..." \
  -H "Content-Type: application/json" \
  -d '{
  "priceId": "00000000-0000-4000-8000-000000000001",
  "successUrl": "https://app.acme.com/callback",
  "cancelUrl": "https://app.acme.com/callback"
}'
```

### Responses

| Status | Description                                              |
| ------ | -------------------------------------------------------- |
| `201`  | Created checkout.                                        |
| `402`  | Organization billing is not active.                      |
| `404`  | Project or payment resource not found.                   |
| `409`  | Duplicate idempotency key or conflicting resource state. |
| `422`  | Invalid payment request.                                 |
| `502`  | The payment processor rejected the request.              |
| `503`  | The payment processor is temporarily unavailable.        |

### Response body (201)

| Field               | Type           | Description                                                                        |
| ------------------- | -------------- | ---------------------------------------------------------------------------------- |
| `id`                | string         | Format: `uuid`.                                                                    |
| `priceId`           | string         | Format: `uuid`.                                                                    |
| `customerId`        | string or null | Format: `uuid`.                                                                    |
| `mode`              | string         | One of: `one_time`, `subscription`.                                                |
| `quantity`          | integer        | —                                                                                  |
| `status`            | string         | One of: `open`, `complete`, `expired`.                                             |
| `paymentStatus`     | string         | One of: `unpaid`, `paid`, `no_payment_required`, `refunded`, `partially_refunded`. |
| `settlementStatus`  | string         | One of: `pending`, `available`, `paid`, `failed`, `not_applicable`.                |
| `url`               | string or null | Hosted checkout URL. Present only on the create response. Format: `uri`.           |
| `clientReferenceId` | string or null | —                                                                                  |
| `expiresAt`         | string         | Format: `date-time`.                                                               |
| `createdAt`         | string         | Format: `date-time`.                                                               |

## customerPayments.runtimeCreateCheckout

Create a hosted checkout from the deployed server runtime scoped to exactly one project and environment.

```text theme={null}
POST /v1/runtime/payments/checkouts
```

* **Auth:** Project app-service token (`Authorization: Bearer $APP_SERVICE_TOKEN`).
* **Billing:** requires active organization billing — returns `402` otherwise.
* **Risk:** `write` · sensitive (elevated blast radius)

### Request body

| Field               | Type    | Required | Description                                                          |
| ------------------- | ------- | -------- | -------------------------------------------------------------------- |
| `priceId`           | string  | Yes      | Format: `uuid`.                                                      |
| `quantity`          | integer | No       | —                                                                    |
| `successUrl`        | string  | Yes      | Format: `uri`.                                                       |
| `cancelUrl`         | string  | Yes      | Format: `uri`.                                                       |
| `customerId`        | string  | No       | Format: `uuid`.                                                      |
| `customerEmail`     | string  | No       | Format: `email`.                                                     |
| `clientReferenceId` | string  | No       | —                                                                    |
| `metadata`          | object  | No       | At most 20 application-defined string entries. Map of string values. |

### Example

```bash theme={null}
curl -X POST "https://api.korve.dev/v1/runtime/payments/checkouts" \
  -H "Authorization: Bearer $APP_SERVICE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "priceId": "00000000-0000-4000-8000-000000000001",
  "successUrl": "https://app.acme.com/callback",
  "cancelUrl": "https://app.acme.com/callback"
}'
```

### Responses

| Status | Description                                              |
| ------ | -------------------------------------------------------- |
| `201`  | Created checkout.                                        |
| `402`  | Organization billing is not active.                      |
| `404`  | Project or payment resource not found.                   |
| `409`  | Duplicate idempotency key or conflicting resource state. |
| `422`  | Invalid payment request.                                 |
| `502`  | The payment processor rejected the request.              |
| `503`  | The payment processor is temporarily unavailable.        |

### Response body (201)

| Field               | Type           | Description                                                                        |
| ------------------- | -------------- | ---------------------------------------------------------------------------------- |
| `id`                | string         | Format: `uuid`.                                                                    |
| `priceId`           | string         | Format: `uuid`.                                                                    |
| `customerId`        | string or null | Format: `uuid`.                                                                    |
| `mode`              | string         | One of: `one_time`, `subscription`.                                                |
| `quantity`          | integer        | —                                                                                  |
| `status`            | string         | One of: `open`, `complete`, `expired`.                                             |
| `paymentStatus`     | string         | One of: `unpaid`, `paid`, `no_payment_required`, `refunded`, `partially_refunded`. |
| `settlementStatus`  | string         | One of: `pending`, `available`, `paid`, `failed`, `not_applicable`.                |
| `url`               | string or null | Hosted checkout URL. Present only on the create response. Format: `uri`.           |
| `clientReferenceId` | string or null | —                                                                                  |
| `expiresAt`         | string         | Format: `date-time`.                                                               |
| `createdAt`         | string         | Format: `date-time`.                                                               |

## customerPayments.listSubscriptions

List webhook-reconciled subscriptions in ascending creation order, with UUID as the stable tie-breaker.

```text theme={null}
GET /v1/orgs/{orgId}/projects/{projectId}/payments/subscriptions
```

* **Auth:** Organization API key (`Authorization: Bearer korve_...`) or dashboard session.
* **Minimum role:** `member`
* **Risk:** `read`

### Query parameters

| Name          | Type    | Required | Description                        |
| ------------- | ------- | -------- | ---------------------------------- |
| `environment` | string  | No       | Pattern: `^[a-z][a-z0-9-]{0,38}$`. |
| `cursor`      | string  | No       | —                                  |
| `limit`       | integer | No       | —                                  |

### Example

```bash theme={null}
curl "https://api.korve.dev/v1/orgs/$ORG_ID/projects/$PROJECT_ID/payments/subscriptions?environment=example" \
  -H "Authorization: Bearer korve_..."
```

### Responses

| Status | Description                                              |
| ------ | -------------------------------------------------------- |
| `200`  | Subscription page.                                       |
| `402`  | Organization billing is not active.                      |
| `404`  | Project or payment resource not found.                   |
| `409`  | Duplicate idempotency key or conflicting resource state. |
| `422`  | Invalid payment request.                                 |
| `502`  | The payment processor rejected the request.              |
| `503`  | The payment processor is temporarily unavailable.        |

### Response body (200)

| Field                       | Type           | Description                                                                   |
| --------------------------- | -------------- | ----------------------------------------------------------------------------- |
| `items`                     | object\[]      | —                                                                             |
| `items[].id`                | string         | Format: `uuid`.                                                               |
| `items[].customerId`        | string or null | Format: `uuid`.                                                               |
| `items[].priceId`           | string or null | Format: `uuid`.                                                               |
| `items[].status`            | string         | One of: `incomplete`, `trialing`, `active`, `past_due`, `canceled`, `unpaid`. |
| `items[].cancelAtPeriodEnd` | boolean        | —                                                                             |
| `items[].currentPeriodEnd`  | string or null | Format: `date-time`.                                                          |
| `items[].createdAt`         | string         | Format: `date-time`.                                                          |
| `items[].updatedAt`         | string         | Format: `date-time`.                                                          |
| `nextCursor`                | string or null | —                                                                             |

## customerPayments.cancelSubscription

Cancel a subscription now or at the end of its current period.

```text theme={null}
POST /v1/orgs/{orgId}/projects/{projectId}/payments/subscriptions/{subscriptionId}/cancel
```

* **Auth:** Organization API key (`Authorization: Bearer korve_...`) or dashboard session.
* **Minimum role:** `admin`
* **Risk:** `write` · sensitive (elevated blast radius)

### Query parameters

| Name          | Type   | Required | Description                        |
| ------------- | ------ | -------- | ---------------------------------- |
| `environment` | string | No       | Pattern: `^[a-z][a-z0-9-]{0,38}$`. |

### Request body

| Field         | Type    | Required | Description |
| ------------- | ------- | -------- | ----------- |
| `atPeriodEnd` | boolean | Yes      | —           |

### Example

```bash theme={null}
curl -X POST "https://api.korve.dev/v1/orgs/$ORG_ID/projects/$PROJECT_ID/payments/subscriptions/$SUBSCRIPTION_ID/cancel?environment=example" \
  -H "Authorization: Bearer korve_..." \
  -H "Content-Type: application/json" \
  -d '{
  "atPeriodEnd": true
}'
```

### Responses

| Status | Description                                              |
| ------ | -------------------------------------------------------- |
| `200`  | Updated subscription.                                    |
| `402`  | Organization billing is not active.                      |
| `404`  | Project or payment resource not found.                   |
| `409`  | Duplicate idempotency key or conflicting resource state. |
| `422`  | Invalid payment request.                                 |
| `502`  | The payment processor rejected the request.              |
| `503`  | The payment processor is temporarily unavailable.        |

### Response body (200)

| Field               | Type           | Description                                                                   |
| ------------------- | -------------- | ----------------------------------------------------------------------------- |
| `id`                | string         | Format: `uuid`.                                                               |
| `customerId`        | string or null | Format: `uuid`.                                                               |
| `priceId`           | string or null | Format: `uuid`.                                                               |
| `status`            | string         | One of: `incomplete`, `trialing`, `active`, `past_due`, `canceled`, `unpaid`. |
| `cancelAtPeriodEnd` | boolean        | —                                                                             |
| `currentPeriodEnd`  | string or null | Format: `date-time`.                                                          |
| `createdAt`         | string         | Format: `date-time`.                                                          |
| `updatedAt`         | string         | Format: `date-time`.                                                          |

## customerPayments.listEvents

List bounded, deduplicated payment lifecycle events in descending receipt order, with UUID as the stable tie-breaker.

```text theme={null}
GET /v1/orgs/{orgId}/projects/{projectId}/payments/events
```

* **Auth:** Organization API key (`Authorization: Bearer korve_...`) or dashboard session.
* **Minimum role:** `member`
* **Risk:** `read`

### Query parameters

| Name          | Type    | Required | Description                        |
| ------------- | ------- | -------- | ---------------------------------- |
| `environment` | string  | No       | Pattern: `^[a-z][a-z0-9-]{0,38}$`. |
| `cursor`      | string  | No       | —                                  |
| `limit`       | integer | No       | —                                  |

### Example

```bash theme={null}
curl "https://api.korve.dev/v1/orgs/$ORG_ID/projects/$PROJECT_ID/payments/events?environment=example" \
  -H "Authorization: Bearer korve_..."
```

### Responses

| Status | Description                                              |
| ------ | -------------------------------------------------------- |
| `200`  | Event page.                                              |
| `402`  | Organization billing is not active.                      |
| `404`  | Project or payment resource not found.                   |
| `409`  | Duplicate idempotency key or conflicting resource state. |
| `422`  | Invalid payment request.                                 |
| `502`  | The payment processor rejected the request.              |
| `503`  | The payment processor is temporarily unavailable.        |

### Response body (200)

| Field                      | Type           | Description                                                        |
| -------------------------- | -------------- | ------------------------------------------------------------------ |
| `items`                    | object\[]      | —                                                                  |
| `items[].id`               | string         | Format: `uuid`.                                                    |
| `items[].type`             | string         | —                                                                  |
| `items[].processingStatus` | string         | One of: `pending`, `processing`, `processed`, `ignored`, `failed`. |
| `items[].occurredAt`       | string         | Format: `date-time`.                                               |
| `items[].receivedAt`       | string         | Format: `date-time`.                                               |
| `nextCursor`               | string or null | —                                                                  |
