---
title: "Downgrade — release the N newest live mailboxes and lower the billed quantity."
method: POST
path: "/remove-mailboxes"
tags: ["infrastructure"]
---

# Downgrade — release the N newest live mailboxes and lower the billed quantity.

`POST /remove-mailboxes`

## Request

- Base URL: `https://api.coldrig.dev`
- URL: `https://api.coldrig.dev/remove-mailboxes`
- Auth: HTTP bearer

Releases `count` MORE mailboxes than are currently released (RELATIVE, not a target fleet size) and syncs the lower Stripe quantity (proration_behavior 'none' — no mid-cycle credit; the lower price takes effect at the next renewal, minimum 3 mailboxes / $49; an account already paying keeps its own minimum). `acknowledged: true` is the quoted, irreversible-this-cycle consent this call requires.

A keyed call's FIRST attempt resolves and records WHICH mailboxes to release; every later call under the SAME Idempotency-Key can only ever finish that recorded set, whatever `count` it sends — a genuine second downgrade needs a NEW key. `releasedCount` can be less than the requested `count`: `failedCount` names how many the provider refused (still live, still billed) and `unreleased` lists those addresses — read them before concluding a downgrade landed. A 409 means a release is already running for this account.

## Headers

- `Idempotency-Key` string

## Request body

- object
  - `count` integer, required: How many MORE mailboxes to release.
  - `acknowledged` true, required: Quoted

## Response `200`

Release outcome (partial or complete — see `failedCount`/`unreleased`).

- RemoveMailboxesResult: `releasedCount` is what COMPLETED this call and can be less than the requested `count`; `failedCount` names how many the provider refused (STILL LIVE, still billed) and `unreleased` lists those addresses (length === failedCount). `billing` is the projected monthly after the release (the lower count, never below the account's minimum price). `deduplicated: true` means this call did NOT re-perform the downgrade — it replayed an earlier call's already-recorded outcome under the same Idempotency-Key (the count/billing fields describe that earlier outcome, not new work done just now). This can happen even after the 30-day request-idempotency window itself has expired: the underlying release record persists independently, so a reused key still reports no re-release rather than silently starting a fresh one.
  - `releasedCount` integer, required
  - `failedCount` integer, required
  - `unreleased` string[], required
  - `billing` MailboxBilling, required: Billing projection returned on every mailbox add/remove (no silent capacity addition). `provisionedAfter` is the live mailbox count AFTER the call — reality, not the ask (a capacity-limited partial or a quoteOnly preview both report the true resulting count).
    - `provisionedAfter` integer, required: Live set-up mailbox count after this operation.
    - `projectedMonthlyCents` integer, required: Projected monthly: $49 a month covers the first 3 mailboxes, then $5 a month for each extra mailbox (3-mailbox minimum) — folding any checkout discount.
    - `formula` string, required
    - `domainEnding` object: Only on a quoteOnly preview of an order over 10 mailboxes in the account's first billing month that buys new domains, when the platform's .co switch is on: they will end in .co unless the real call sends `dotComDomains: true`.
      - `ending` 'co', required
      - `newDomains` integer, required: New domains this order buys.
      - `feeCentsEach` integer, required: The one-time fee per .com domain bought, in cents, added to the next bill.
      - `text` string, required: The same
    - `prepay` object: Only on a quoteOnly preview of an order making more than 4 new domains in your first billing month, when the platform's prepaid-month switch is on: the real call must send `prepaySecondMonth: true` (else 400 `prepay_required`, nothing bought); month 2 is then charged right after the mailboxes are set up, at most `cents`.
      - `months` 2, required: The month paid up front is the second one.
      - `cents` integer, required: Month 2 at this order's mailbox count.
      - `text` string, required: The same
  - `deduplicated` boolean, required
  - `nextSteps` NextSteps: What this account should do next, derived fresh on every response that carries it (design §7.2) — the one endpoint-agnostic self-guiding contract shared by setup_infrastructure and infrastructure_status.
    - `status` 'owed' | 'none_owed', required: EXPLICIT discriminator — never inferred from `steps` being empty. `none_owed` is routinely emitted WITH a non-empty `steps` array (an `available` step is not owed).
    - `steps` NextStep[], required
      - `reason` 'paid_seats_unprovisioned' | 'seat_headroom_free' | 'domain_dns_incomplete' | 'ordinal_incomplete' | 'ordinal_slot_shortfall' | 'ordinal_domain_retired' | 'setup_operator_blocked' | 'setup_capacity_held' | 'large_order_payment_pending' | 'billed_quantity_drift' | 'account_frozen' | 'message_action_required' | 'mailbox_credentials_pending' | 'ready_to_launch', required: Closed union (design §7.2). paid_seats_unprovisioned = paying, nothing set up (owed). seat_headroom_free = at the account's mailbox minimum with fewer set up, the remainder cost $0 (NEVER owed). domain_dns_incomplete = a live domain's mail DNS has not come up yet. ordinal_incomplete = a requested domain ordinal never completed. ordinal_slot_shortfall = the domain landed and its mail DNS is up, but it holds fewer mailboxes than the call that created it asked for — mailbox slots complete independently, so repeating the setup call creates exactly the missing ones. ordinal_domain_retired = a committed ordinal's domain was burned by the platform itself and has not yet been automatically replaced; no self-serve call brings it back — contact Coldrig support. setup_operator_blocked = the platform stopped; only someone at Coldrig can restart it. setup_capacity_held = setup is paused while Coldrig support checks recent purchases on this account — nothing failed and nothing was charged. large_order_payment_pending = a large order waits on its payment page (owed until it is paid or expires): a person with the card pays it, then the same call repeats; nothing was bought. billed_quantity_drift = our Stripe push did not land, ours to fix. account_frozen = lifecycle freeze. With no subscription still running, re-checkout (POST /checkout) is the way out. With one still running, a second checkout is refused (it would charge twice), and the way out is the billing portal (POST /billing/portal): update the card when behind on payment, or renew the subscription when a cancellation is scheduled. An open or lost dispute (or a `disputed` billing state) bars checkout: contact Coldrig support. message_action_required = an unacked message the account will not progress past. mailbox_credentials_pending = a set-up mailbox's credentials have not reached the sending engine yet. ready_to_launch = infrastructure is up, no campaign exists yet.
      - `kind` 'owed' | 'available', required: owed = the account will not progress until this happens. available = nothing is blocked; this is the next thing worth doing.
      - `why` string, required: One customer-safe sentence. Vendor-blind: composed prose, never a raw error message.
      - `action` NextStepAction, required: A DISCRIMINATED UNION on `via` — always present, so "no action is possible" is a stated value rather than an empty object.
        - `via` 'mcp_tool' | 'http' | 'none', required
        - `tool` 'setup_infrastructure' | 'launch_campaign' | 'contact_operator' | 'ack_message' | 'configure_byo_domain': Present when via=mcp_tool. A real MCP tool name.
        - `params` object: Present when via=mcp_tool. Literal call arguments, complete EXCEPT the names listed in paramsToSupply.
        - `paramsToSupply` string[]: Present when via=mcp_tool. Fields the platform cannot know; the caller must add them. Empty = send params verbatim.
        - `idempotencyKey` unknown
        - `method` string: Present when via=http.
        - `path` string: Present when via=http.
        - `note` string: Present when via=http or via=none — who acts / what unblocks it.
      - `waitingOn` unknown, required
      - `notBeforeMs` integer, required: Do not repeat this call before notBeforeMs from now — the retry cadence as a machine field. 0 = now.
      - `effect` MailboxBilling, required: Billing projection returned on every mailbox add/remove (no silent capacity addition). `provisionedAfter` is the live mailbox count AFTER the call — reality, not the ask (a capacity-limited partial or a quoteOnly preview both report the true resulting count).
        - `provisionedAfter` integer, required: Live set-up mailbox count after this operation.
        - `projectedMonthlyCents` integer, required: Projected monthly: $49 a month covers the first 3 mailboxes, then $5 a month for each extra mailbox (3-mailbox minimum) — folding any checkout discount.
        - `formula` string, required
        - `domainEnding` object: Only on a quoteOnly preview of an order over 10 mailboxes in the account's first billing month that buys new domains, when the platform's .co switch is on: they will end in .co unless the real call sends `dotComDomains: true`.
          - `ending` 'co', required
          - `newDomains` integer, required: New domains this order buys.
          - `feeCentsEach` integer, required: The one-time fee per .com domain bought, in cents, added to the next bill.
          - `text` string, required: The same
        - `prepay` object: Only on a quoteOnly preview of an order making more than 4 new domains in your first billing month, when the platform's prepaid-month switch is on: the real call must send `prepaySecondMonth: true` (else 400 `prepay_required`, nothing bought); month 2 is then charged right after the mailboxes are set up, at most `cents`.
          - `months` 2, required: The month paid up front is the second one.
          - `cents` integer, required: Month 2 at this order's mailbox count.
          - `text` string, required: The same
      - `sinceMs` unknown, required
    - `computedAt` integer, required

## Other responses

- `400`: Request body failed schema validation.
- `401`: Missing, invalid, or inactive bearer token.
- `409`: The work this request asks for is already running — either a call with this Idempotency-Key has not finished yet, or (on /remove-mailboxes, where only one release may run at a time per account) an unkeyed release is in flight, or (on /setup-infrastructure, only where Coldrig has switched on background completion) that completion is running right now. Retryable by design: retry once the first call has recorded its result. After a refused release, re-read your live mailbox count before deciding to retry — the call that is running may already be doing what you asked.

## Changes

- **2026-10-19** `242a462d82c4`: 1 warning, 4 info
  - added the new `large_order_payment_pending` enum value to the `nextSteps/steps/items/reason` response property for the response status `200`
  - added the optional property `billing/domainEnding` to the response with the `200` status
  - added the optional property `billing/prepay` to the response with the `200` status
  - added the optional property `nextSteps/steps/items/effect/oneOf[#/components/schemas/MailboxBilling]/domainEnding` to the response with the `200` status
  - …1 more

[Operation history](https://skmtc.dev/coldrig/apis/agent-cold-email-api/changes/remove-mailboxes/post.md)

---

[API](https://skmtc.dev/coldrig/apis/agent-cold-email-api.md) · [All operations](https://skmtc.dev/coldrig/apis/agent-cold-email-api/llms.txt) · [OpenAPI document](https://skmtc.dev/coldrig/apis/agent-cold-email-api/revisions/242a462d82c4?raw)
