> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developers.upwardli.com/llms.txt.

# CBaaS Tier Upgrades and Downgrades

## Overview

Once a Credit Builder as a Service (CBaaS) consumer's credit line is open, you can move them to a higher or lower tier by changing their credit limit with `POST /v2/credit-lines/{credit_line_id}/`.

The consumer starts with the credit limit set when their loan is submitted at **Step 6: Submit Loan**. This page covers changing that limit afterwards.

> **Note**
>
> Tier upgrades and downgrades are enabled per program. Contact your Upward representative to turn them on and to agree on your tier ranges and cooldown period. Until then, this endpoint returns `403 Forbidden`.

### Lifecycle at a Glance

| Stage                         | What you do                                                     | API                                                                         |
| ----------------------------- | --------------------------------------------------------------- | --------------------------------------------------------------------------- |
| Step 6: Submit Loan           | Upward opens the credit line with the consumer's starting limit | Submit Loan                                                                 |
| After the credit line is open | Upgrade or downgrade the limit                                  | `POST /v2/credit-lines/{credit_line_id}/` with `tier_update`                |
| After each change             | Confirm the new limit                                           | `creditline.updated` webhook, then `GET /v2/credit-lines/{credit_line_id}/` |

---

## When to Use This

Use this endpoint when an onboarded consumer moves to a higher tier (for example, after a run of on-time payments) or to a lower tier (for example, after missed payments or a change in your risk assessment).

## Before You Start

Check all of these before you send a tier change:

* **Your program is enabled for tier changes.** Upward turns this on per program. If it isn't, the endpoint returns `403`.
* **Your token has a write scope.** The partner access token needs `api:trade-line:write` or `api:write`.
* **The credit line is open.** The consumer must have completed **Step 6: Submit Loan**. Tier changes aren't available before then.
* **You have the credit line `id`.** It is returned by the onboarding flow and on the `creditline.*` webhooks.

## The Request

POST `https://api-sandbox.upwardli.com/v2/credit-lines/{credit_line_id}/`

See the [update credit line tier](/api-access/api-reference/credit-lines/create-credit-line-tier-update) API.

```json
Header
{
    "Authorization":"Bearer [access_token]",
    "Content-Type":"application/json"
}
```

Body

```json
{
  "tier_update": {
    "approved_loan_amount": 2500,
    "reason": "Moved to Gold tier after 6 on-time payments"
  }
}
```

| Field                              | Type    | Required | Description                                                                                                                                                               |
| ---------------------------------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tier_update.approved_loan_amount` | integer | Yes      | The new credit limit in whole US dollars. Must be a JSON integer within the range allowed by your tier configuration. Quoted numbers, decimals and booleans are rejected. |
| `tier_update.reason`               | string  | No       | Why the limit changed, up to 255 characters. Upward stores it with the change for audit and support. Don't include personal or sensitive consumer data.                   |

The same endpoint handles both directions. A higher amount than the current limit is an **upgrade**; a lower amount is a **downgrade**.

## The Response

A successful call returns `200 OK` with the updated credit line, in the same shape as [`GET /v2/credit-lines/{credit_line_id}/`](/api-access/api-reference/credit-lines/retrieve-get-credit-line):

```json
{
  "id": "0f3c9a52-8d1e-4b7a-9a7e-2f1d6c4e8b10",
  "consumer_id": "a51e7c2b-3f4d-4e8a-b6c1-9d0e2f3a4b5c",
  "current_status": "active",
  "outstanding_balance": 420.0,
  "approved_loan_amount": 2500.0,
  "agreement_accepted": true,
  "autopay_enabled": true
}
```

`approved_loan_amount` is the consumer's credit limit and now shows the new value.

---

## Walkthrough: Upgrade a Consumer

In this example, a consumer was onboarded at $1,500 and has earned an upgrade to $2,500.

**Step 1: Read the current credit line.**

GET `https://api-sandbox.upwardli.com/v2/credit-lines/{credit_line_id}/`

```json
{
  "id": "0f3c9a52-8d1e-4b7a-9a7e-2f1d6c4e8b10",
  ...
  "outstanding_balance": 420.0,
  "approved_loan_amount": 1500.0,
  ...
}
```

Confirm the current limit and decide the new one. If the consumer has purchases that are still `InProgress` or `Received`, wait until they settle (see [Pending transactions](#pending-transactions)).

**Step 2: Send the upgrade.**

POST `https://api-sandbox.upwardli.com/v2/credit-lines/{credit_line_id}/`

```json
{
  "tier_update": {
    "approved_loan_amount": 2500,
    "reason": "Upgrade: 6 on-time payments"
  }
}
```

The response is `200 OK` with `"approved_loan_amount": 2500.0`.

**Step 3: Handle the webhook.**

Upward sends a `creditline.updated` webhook. Its `resources` array points to the credit line. Fetch the credit line and update the limit you store and show in your app.

**What changes for the consumer:**

* New purchase holds are checked against the new $2,500 limit, so the consumer has $1,000 more available credit right away.
* Statements generated after the change show \$2,500 as the credit limit.
* The outstanding balance doesn't change.
* The cooldown starts. The consumer's limit can't change again until the cooldown period ends.

---

## Walkthrough: Downgrade a Consumer

In this example, a consumer at $2,500 moves down to $1,000.

**Step 1: Read the current credit line** and note the `outstanding_balance`.

**Step 2: Send the downgrade.**

POST `https://api-sandbox.upwardli.com/v2/credit-lines/{credit_line_id}/`

```json
{
  "tier_update": {
    "approved_loan_amount": 1000,
    "reason": "Downgrade: tier review"
  }
}
```

The response is `200 OK` with `"approved_loan_amount": 1000.0`.

**Step 3: Handle the `creditline.updated` webhook** the same way as an upgrade.

### Downgrading Below the Current Balance

Upward doesn't block a downgrade below the consumer's current balance. If the consumer owes $1,200 and you downgrade the limit to $1,000:

* the downgrade succeeds, and the balance stays at \$1,200
* existing transactions aren't reversed
* new purchase holds are declined with `400 Bad Request` ("insufficient funds in their Credit Line") until the balance plus the new hold fits within \$1,000
* the consumer keeps repaying as usual

> **Warning**
>
> **Tell the consumer before you downgrade.** A downgrade below the balance means new purchases are declined until they pay down. Check `outstanding_balance` first, and decide whether to wait for a lower balance or tell the consumer what to expect.

---

## Rules for Tier Changes

### Cooldown Between Changes

After a limit change, the consumer's limit can't change again until the cooldown period ends. The cooldown is set per program and is **30 days** unless you've agreed a different period with Upward.

* The cooldown applies to upgrades and downgrades alike. An upgrade followed by a downgrade inside the window is rejected.
* Setting the starting limit at onboarding does **not** start the cooldown. The first upgrade or downgrade after onboarding can happen at any time.
* A change inside the cooldown returns `409 Conflict`. The message gives the next date a change is allowed (UTC), for example `Credit limit can be changed again on 2026-12-06.`

If you need an exception, such as a correction made in error, contact Upward support.

### Pending Transactions

A limit change is rejected with `409 Conflict` while the consumer has transactions with status `InProgress` or `Received`. Wait for them to settle and try again.

Transactions in `Hold` status don't block a change.

### Retries Are Safe

If you send the consumer's current limit, Upward returns `200 OK` and changes nothing: no webhook is sent and the cooldown doesn't start. You can safely retry a request if you didn't get a response, for example after a timeout.

If two different changes arrive for the same consumer at the same time, Upward processes them one after the other. With a cooldown in place, the first is applied and the second is rejected with `409 Conflict`.

### Allowed Values

The new limit must be a whole-dollar JSON integer within the range allowed by your tier configuration. Quoted numbers such as `"1500"`, decimals such as `1500.5` or `1500.0`, and booleans are rejected with `400 Bad Request`. You can move a consumer up or down by any amount within that range. You don't have to step through each tier.

---

## Recommended Integration Pattern

1. **Store the credit line `id`** when the loan is submitted.
2. **When your tier logic decides to move a consumer**, read the credit line, check `outstanding_balance` and pending transactions, then `POST` the `tier_update`.
3. **Treat `200 OK` as done.** On a timeout, retry with the same amount.
4. **On `409 Conflict`**, read the message. For a cooldown, schedule the change for the date given. For pending transactions, retry after they settle.
5. **Listen for `creditline.updated`** and refresh the limit you show from `GET /v2/credit-lines/{credit_line_id}/`. The same webhook fires for other credit line updates, so always read `approved_loan_amount` instead of assuming what changed.

---

---

## Errors

| Status            | Message                                                         | Cause                                                                                                                                          | What to do                                    |
| ----------------- | --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- |
| `400 Bad Request` | Field errors under `detail`                                     | `tier_update` is missing, `approved_loan_amount` isn't a whole-dollar JSON integer within your tier range, or `reason` is over 255 characters. | Fix the body and resend.                      |
| `403 Forbidden`   | `Credit limit changes are not enabled for this program.`        | Tier changes aren't enabled for your program.                                                                                                  | Contact your Upward representative.           |
| `403 Forbidden`   | Permission error                                                | Your token doesn't have `api:trade-line:write` or `api:write`.                                                                                 | Request a token with one of those scopes.     |
| `404 Not Found`   |                                                                 | No credit line with that `id` exists for your partner account.                                                                                 | Check the `credit_line_id`.                   |
| `409 Conflict`    | `Credit limit can only be changed after the loan is created.`   | Step 6: Submit Loan hasn't completed for this consumer.                                                                                        | Wait until the loan is submitted, then retry. |
| `409 Conflict`    | `Credit limit can be changed again on YYYY-MM-DD.`              | The last change was inside the cooldown period.                                                                                                | Retry on or after the date given.             |
| `409 Conflict`    | `Credit limit can't be changed while transactions are pending.` | The consumer has `InProgress` or `Received` transactions.                                                                                      | Retry after they settle.                      |

A `400` response lists the field errors:

```json
{
  "detail": {
    "tier_update": {
      "approved_loan_amount": ["A valid integer is required."]
    }
  },
  "code": 400,
  "messages": []
}
```

`409` responses, and the `403` for a program without tier changes, use this shape:

```json
{
  "error": "Credit limit can be changed again on 2026-12-06.",
  "error_code": "api_error",
  "message": []
}
```

A rejected request changes nothing: the limit, the cooldown and the webhooks are all left as they were.