# Adjust a customer's credit balance

> The customers.credit_balance.adjustments.create operation: POST /api/v1/customers/{id}/credit_balance/adjustments in the REST API and the customers_credit_balance_adjustments_create tool in the MCP server.

Adds credit to a customer's invoice credit balance (a goodwill credit), or takes unused credit back, with a
reason. A positive `amount` adds, a negative one removes; the balance never goes below zero
(`credit_balance_insufficient`). The credit is applied automatically to the customer's next invoices in that
currency, after tax.

In the ledger, credit granted without payment is a cost (`promo_expense`). Financial tier: owners and admins
(`customers:admin`) with a sign-in in the last 15 minutes; an approval policy may require a second person above
an amount. Dry-run it first. Emits `customer.credit_balance.updated`.

## At a glance

- **REST:** `POST /api/v1/customers/{id}/credit_balance/adjustments`
- **MCP tool:** `customers_credit_balance_adjustments_create`
- **Operation ID:** `customers.credit_balance.adjustments.create`
- **Scope:** Brand
- **Permission:** `customers:admin`
- **Risk tier:** `financial`
- **Dry run:** Supported (send `Ownrecord-Dry-Run: true`)
- **Idempotency:** `Idempotency-Key` required
- **Modes:** Test and live
- **Authentication:** API key with the `customers:admin` scope
- **Fresh sign-in:** In the console, a sign-in within the last 15 minutes
- **Since:** M5

## Request

### Path parameters

| Field | Type       | Description             |
| ----- | ---------- | ----------------------- |
| `id`  | `cus_…` ID | The customer (`cus_…`). |

### Body

A JSON object (`Content-Type: application/json`):

| Field    | Type   | Required | Description                                                                                |
| -------- | ------ | -------- | ------------------------------------------------------------------------------------------ |
| `amount` | money  | Yes      | Signed: a positive amount adds credit (goodwill), a negative one takes unused credit back. |
| `reason` | string | Yes      | Why (required): shown in the ledger, the audit log and to approvers.                       |

## Response

`201 Created` with a JSON object:

| Field                 | Type                          | Description                              |
| --------------------- | ----------------------------- | ---------------------------------------- |
| `object`              | `"credit_balance_adjustment"` |                                          |
| `customerId`          | `cus_…` ID                    |                                          |
| `amount`              | money                         | The signed adjustment.                   |
| `balance`             | money                         | The balance in that currency afterwards. |
| `reason`              | string                        |                                          |
| `ledgerTransactionId` | `txn_…` ID                    |                                          |

## Examples

### A goodwill credit after an outage

```http
POST /api/v1/customers/cus_01m4tx5nj8emwbyf7y9vcf47ym/credit_balance/adjustments
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
Content-Type: application/json

{
  "amount": {
    "amount": "250000000",
    "currency": "EUR"
  },
  "reason": "Goodwill for the September outage"
}
```

Response (`201`):

```json
{
  "object": "credit_balance_adjustment",
  "customerId": "cus_01m4tx5nj8emwbyf7y9vcf47ym",
  "amount": {
    "amount": "250000000",
    "currency": "EUR"
  },
  "balance": {
    "amount": "250000000",
    "currency": "EUR"
  },
  "reason": "Goodwill for the September outage",
  "ledgerTransactionId": "txn_01m4tx5nj8emwbyf7y9vcf47yq"
}
```

## Errors

| Code                                                                                                       | HTTP status | When                                                                                    | What to do                                                                                                                                                                                                                                                                            |
| ---------------------------------------------------------------------------------------------------------- | ----------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed)                     | 400         | The amount is zero, not whole minor units or malformed, or the reason is missing.       | Fix the fields listed in `details.issues` and send the request again.                                                                                                                                                                                                                 |
| [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found)                   | 404         | This brand has no such customer in this mode.                                           | Check the ID and that your credentials are scoped to the right organization, entity, brand and mode.                                                                                                                                                                                  |
| [`customer_deleted`](https://ownrecord.io/docs/reference/errors.md#customer_deleted)                       | 400         | The customer was deleted.                                                               | Create a new customer for this buyer. Deleted customers stay readable for the records they belong to, but can't be changed.                                                                                                                                                           |
| [`credit_balance_insufficient`](https://ownrecord.io/docs/reference/errors.md#credit_balance_insufficient) | 402         | A negative adjustment takes back more than the balance (`details.balance`).             | Take back at most the current balance (`details.balance`, integer micros, in `details.currency`). The credit balance never goes below zero.                                                                                                                                           |
| [`idempotency_key_reused`](https://ownrecord.io/docs/reference/errors.md#idempotency_key_reused)           | 409         | The idempotency key was used before for another request.                                | Use a new Idempotency-Key for a different request, or resend the original request unchanged.                                                                                                                                                                                          |
| [`version_conflict`](https://ownrecord.io/docs/reference/errors.md#version_conflict)                       | 409         | Another posting moved the balance at the same moment; retry.                            | Fetch the object again and retry the change.                                                                                                                                                                                                                                          |
| [`approval_required`](https://ownrecord.io/docs/reference/errors.md#approval_required)                     | 202         | The organization's approval policy needs a second person for this; nothing changed yet. | Nothing ran yet. An approver reviews `approval.summary` and the dry-run result in the console (`approval.approveUrl`). Poll `GET /approvals/{id}` or watch for the `approval.resolved` event; sending the same request again with the same Idempotency-Key returns the same approval. |
| [`approval_rejected`](https://ownrecord.io/docs/reference/errors.md#approval_rejected)                     | 409         | The same request was sent again after it was rejected.                                  | Read the reason in `details.approval.rejection`. To try again, send a new request with a new Idempotency-Key.                                                                                                                                                                         |
| [`approval_expired`](https://ownrecord.io/docs/reference/errors.md#approval_expired)                       | 409         | The same request was sent again after its approval request expired.                     | Send the request again with a new Idempotency-Key to create a new approval request. Requests expire after 72 hours.                                                                                                                                                                   |
| [`stale_approval`](https://ownrecord.io/docs/reference/errors.md#stale_approval)                           | 409         | The approved request could no longer run as requested.                                  | Check the current state (`details.approval.failure` says what failed), then send a new request with a new Idempotency-Key.                                                                                                                                                            |
| [`fresh_auth_required`](https://ownrecord.io/docs/reference/errors.md#fresh_auth_required)                 | 403         | Your last sign-in is older than 15 minutes.                                             | Sign in again, then repeat the action within 15 minutes.                                                                                                                                                                                                                              |
| [`scope_required`](https://ownrecord.io/docs/reference/errors.md#scope_required)                           | 400         | The request names no brand (an org or entity key without `Ownrecord-Brand`).            | Call the operation with credentials or a target at its level: an organization, a legal entity, a brand or the platform (`details.required_scope`).                                                                                                                                    |
| [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied)                     | 403         | You can't adjust credit balances (`customers:admin`: owners and admins).                | Ask an organization owner or admin for a role or scope that allows this action.                                                                                                                                                                                                       |
| [`livemode_not_allowed`](https://ownrecord.io/docs/reference/errors.md#livemode_not_allowed)               | 403         | You have no live-mode access, so you can't use live invoices.                           | Use test mode, or credentials for the target's mode. Developers need live access granted by an organization owner or admin.                                                                                                                                                           |

It can also return these [common errors](https://ownrecord.io/docs/reference.md#common-errors): [`api_version_unsupported`](https://ownrecord.io/docs/reference/errors.md#api_version_unsupported), [`invalid_json`](https://ownrecord.io/docs/reference/errors.md#invalid_json), [`unsupported_media_type`](https://ownrecord.io/docs/reference/errors.md#unsupported_media_type), [`authentication_required`](https://ownrecord.io/docs/reference/errors.md#authentication_required), [`api_key_invalid`](https://ownrecord.io/docs/reference/errors.md#api_key_invalid), [`idempotency_key_required`](https://ownrecord.io/docs/reference/errors.md#idempotency_key_required), [`idempotency_in_progress`](https://ownrecord.io/docs/reference/errors.md#idempotency_in_progress), [`approval_policy_denied`](https://ownrecord.io/docs/reference/errors.md#approval_policy_denied), [`rate_limited`](https://ownrecord.io/docs/reference/errors.md#rate_limited), [`internal_error`](https://ownrecord.io/docs/reference/errors.md#internal_error).
