---
title: "Set auto top-up and the monthly spending cap."
method: PATCH
path: "/credits/billing"
tags: ["Credits"]
---

# Set auto top-up and the monthly spending cap.

`PATCH /credits/billing`

## Request

- Base URL: `https://docs.sleeperhit.studio/api/v1`
- URL: `https://docs.sleeperhit.studio/api/v1/credits/billing`
- Auth: HTTP bearer

Auto top-up adds one pack whenever the balance falls below a threshold, charged to the account's saved top-up card, and only within the monthly spending cap (whole US dollars charged for credits per UTC calendar month, net of refunds, counting every pack bought on the Billing page and every automatic top-up, and an attempt still being paid; the plan itself is not counted). A refunded automatic top-up turns auto top-up off. Turning it on needs a saved card (saved in the app for $0, or by a top-up; send the writer to `fixUrl` when there is none), a plan that allows top-ups and a cap of at least the pack price; the threshold must be below the pack's credits. Buying credits happens only in the app: send the writer to `topupUrl`. TWO CALLS: without `confirmed: true` it answers `{ quotedOnly: true, wouldBe, note }` and changes nothing; show the writer the note, and call again with `confirmed: true` once they say go. `autoTopup: { enabled: false }` turns it off. 409 `credit_billing_invalid` for a setting the account cannot honor (`details.problem`: `no_card` — a top-up in the app saves the card, `details.fixUrl` opens it; `no_cap` / `cap_below_pack` with `details.suggestedCapUsd`; `threshold_too_high` / `threshold_too_low`; `unknown_pack`; `plan_without_topups`; `topups_held`). Read the result on GET /credits (`credits.billing`). Scope: `account:write`. Free.

## Request body

- UpdateCreditBillingRequest
  - `autoTopup` object
    - `enabled` boolean, required
    - `thresholdCredits` integer: Below the pack's credits. Defaults to the current threshold, or 100 (less than the pack).
    - `packKey` 'PACK_10' | 'PACK_25' | 'PACK_50' | 'PACK_75' | 'PACK_100': Defaults to the current pack, or PACK_25.
  - `spendCapUsd` integer, nullable: Whole dollars a month, or null for no cap (auto top-up needs one).
  - `confirmed` boolean: Without `true` the call only answers what the settings would be (`quotedOnly: true`).

## Response `200`

What the settings would be, or are now.

- UpdateCreditBillingResult
  - `quotedOnly` boolean, required: True: nothing changed; `wouldBe` is what the settings would be.
  - `wouldBe` CreditBilling: How credits are bought. Auto top-up adds one pack whenever the balance falls below a threshold, charged to the account's saved top-up card, and only within the monthly spending cap (whole US dollars charged for credits per UTC calendar month, net of refunds, counting every pack bought on the Billing page and every automatic top-up, and an attempt still being paid; the plan itself is not counted). A refunded automatic top-up turns auto top-up off. Turning it on needs a saved card (saved in the app for $0, or by a top-up; send the writer to `fixUrl` when there is none), a plan that allows top-ups and a cap of at least the pack price; the threshold must be below the pack's credits. Buying credits happens only in the app: send the writer to `topupUrl`.
    - `card` object, nullable, required: The saved top-up card (brand, last 4, expiry), which one-tap and auto top-up charge. Saved in the app with no charge, or by a top-up; null until one is saved.
      - `brand` string, required: e.g. `visa`.
      - `last4` string, required
      - `expMonth` integer, nullable, required
      - `expYear` integer, nullable, required
    - `spendCap` object, required: The monthly spending cap: whole US dollars charged to a card for credits per UTC calendar month, net of refunds (the plan itself is not counted).
      - `capUsd` integer, nullable, required: The cap the writer set, or null for none.
      - `spentUsd` number, required: Charged (or being charged) for credits so far this month.
      - `from` string, date-time, required
      - `resetsAt` string, date-time, required: The 1st of next month, 00:00 UTC.
    - `autoTopup` object, required
      - `enabled` boolean, required
      - `thresholdCredits` integer, nullable, required: When the balance falls below this…
      - `packKey` 'PACK_10' | 'PACK_25' | 'PACK_50' | 'PACK_75' | 'PACK_100' | 'null', nullable, required: …this pack is added.
      - `packUsd` integer, nullable, required
      - `packCredits` integer, nullable, required
      - `offReason` 'authentication_required' | 'card_declined' | 'card_removed' | 'dispute' | 'plan_ended' | 'refunded' | 'null', nullable, required: Why auto top-up turned itself off (null while on, or when the writer turned it off).
      - `offAt` string, date-time, nullable, required
    - `topupUrl` string, uri, required: Where the writer buys credits: the app's Billing page with the pack's top-up open. No API call buys credits.
  - `changed` boolean
  - `billing` CreditBilling: How credits are bought. Auto top-up adds one pack whenever the balance falls below a threshold, charged to the account's saved top-up card, and only within the monthly spending cap (whole US dollars charged for credits per UTC calendar month, net of refunds, counting every pack bought on the Billing page and every automatic top-up, and an attempt still being paid; the plan itself is not counted). A refunded automatic top-up turns auto top-up off. Turning it on needs a saved card (saved in the app for $0, or by a top-up; send the writer to `fixUrl` when there is none), a plan that allows top-ups and a cap of at least the pack price; the threshold must be below the pack's credits. Buying credits happens only in the app: send the writer to `topupUrl`.
    - `card` object, nullable, required: The saved top-up card (brand, last 4, expiry), which one-tap and auto top-up charge. Saved in the app with no charge, or by a top-up; null until one is saved.
      - `brand` string, required: e.g. `visa`.
      - `last4` string, required
      - `expMonth` integer, nullable, required
      - `expYear` integer, nullable, required
    - `spendCap` object, required: The monthly spending cap: whole US dollars charged to a card for credits per UTC calendar month, net of refunds (the plan itself is not counted).
      - `capUsd` integer, nullable, required: The cap the writer set, or null for none.
      - `spentUsd` number, required: Charged (or being charged) for credits so far this month.
      - `from` string, date-time, required
      - `resetsAt` string, date-time, required: The 1st of next month, 00:00 UTC.
    - `autoTopup` object, required
      - `enabled` boolean, required
      - `thresholdCredits` integer, nullable, required: When the balance falls below this…
      - `packKey` 'PACK_10' | 'PACK_25' | 'PACK_50' | 'PACK_75' | 'PACK_100' | 'null', nullable, required: …this pack is added.
      - `packUsd` integer, nullable, required
      - `packCredits` integer, nullable, required
      - `offReason` 'authentication_required' | 'card_declined' | 'card_removed' | 'dispute' | 'plan_ended' | 'refunded' | 'null', nullable, required: Why auto top-up turned itself off (null while on, or when the writer turned it off).
      - `offAt` string, date-time, nullable, required
    - `topupUrl` string, uri, required: Where the writer buys credits: the app's Billing page with the pack's top-up open. No API call buys credits.
  - `note` string, required: One sentence to read to the writer.

## Other responses

- `400`: Request payload failed validation. The error message names the offending field.
- `401`: Missing, malformed, revoked, or expired API key.
- `403`: API key is valid but lacks the scope, is blocked by IP allowlist, or the account is disabled.
- `409`: A setting the account cannot honor (`credit_billing_invalid`), and nothing changed. `details` is CreditBillingInvalidDetails: `problem` says what to fix (`no_card`, `no_cap`, `cap_below_pack`, `threshold_too_high`, `threshold_too_low`, `unknown_pack`, `plan_without_topups`, `topups_held`) and `fixUrl` where; `suggestedCapUsd` or `maxThresholdCredits` when they apply.
- `429`: Rate limit for this API key + IP + route family was exceeded.
- `500`: The API failed unexpectedly. Safe to retry with the same Idempotency-Key.

---

[API](https://skmtc.dev/sleeperhit/apis/sleeper-hit-studio-story-api.md) · [All operations](https://skmtc.dev/sleeperhit/apis/sleeper-hit-studio-story-api/llms.txt) · [OpenAPI document](https://skmtc.dev/sleeperhit/apis/sleeper-hit-studio-story-api/revisions/64c1102ccb2f?raw)
