Skip to content

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.

POST/api/v1/customers/{id}/credit_balance/adjustments

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

FieldTypeDescription
idcus_… IDThe customer (cus_…).

Body

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

FieldTypeRequiredDescription
amountmoneyYesSigned: a positive amount adds credit (goodwill), a negative one takes unused credit back.
reasonstringYesWhy (required): shown in the ledger, the audit log and to approvers.

Response

201 Created with a JSON object:

FieldTypeDescription
object"credit_balance_adjustment"
customerIdcus_… ID
amountmoneyThe signed adjustment.
balancemoneyThe balance in that currency afterwards.
reasonstring
ledgerTransactionIdtxn_… 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

CodeHTTP statusWhenWhat to do
validation_failed400The 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_found404This 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_deleted400The 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_insufficient402A 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_reused409The idempotency key was used before for another request.Use a new Idempotency-Key for a different request, or resend the original request unchanged.
version_conflict409Another posting moved the balance at the same moment; retry.Fetch the object again and retry the change.
approval_required202The 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_rejected409The 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_expired409The 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_approval409The 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_required403Your last sign-in is older than 15 minutes.Sign in again, then repeat the action within 15 minutes.
scope_required400The 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_denied403You 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_allowed403You 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: api_version_unsupported, invalid_json, unsupported_media_type, authentication_required, api_key_invalid, idempotency_key_required, idempotency_in_progress, approval_policy_denied, rate_limited, internal_error.