> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trusset.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Set Term Config

> Set the maximum duration, terminal date, grace period and term penalty for new loans

Builds the `setTermConfig` transaction that sets the market's loan terms. Requires `ISSUER_ROLE` on the market, held by the wallet the lender of record named at adoption. Before adoption only the deploying factory could call it, so the API refuses a market that has no lender of record yet.

Read the current terms first with [Get Term Config](/endpoints/lending/get-term-config). The market brings its interest accrual up to date before the new terms apply.

<Warning>
  Every call writes the whole term configuration. An omitted `terminalDueDate` clears the terminal date, and an omitted `termPenaltyBps` sets the term penalty to zero. Only an omitted `termGracePeriodDays` keeps the value the market already runs. Send every term you want to keep.
</Warning>

## Which Loans a Change Reaches

A new maturity reaches new loans only, and nothing here moves an open loan's maturity earlier. Whether the grace period and the penalty also stay with new loans depends on the market's implementation, which the calldata response names as `termStamping`.

On `PER_LOAN`, the current implementation, each loan carries the grace period and term penalty it was signed under, so the whole change reaches new loans only. Loans signed before the market stamped terms still follow the live configuration, and `openLoansFollowingConfig` counts them. On `MARKET_CONFIG`, the earlier implementation, the grace period and the penalty are read at realization, so a change reaches every open termed loan.

When a change shortens the grace period or raises the penalty for loans that follow the live configuration, the response carries an `OPEN_TERMED_LOANS_AFFECTED` warning. Such a borrower can be realized sooner, or at a higher charge, than the terms in force when it borrowed.

## Path Parameters

<ParamField path="marketId" type="string" required>Market ID.</ParamField>

## Body Parameters

<ParamField body="maxLoanDurationDays" type="integer" required>
  How long a new loan may run before it falls due. Integer number of days, 0 to 3650. `0` sets no duration, and with no `terminalDueDate` either it switches terms off for new loans.
</ParamField>

<ParamField body="terminalDueDate" type="integer | string">
  A date every new loan falls due by, whatever its duration. A unix timestamp in seconds, or an ISO date. Must be in the future. With `maxLoanDurationDays` at `0`, every new loan matures on this date. Omitting it clears any terminal date the market has.
</ParamField>

<ParamField body="termGracePeriodDays" type="integer">
  Days between a loan's maturity and the moment it becomes realizable. Integer, 1 to 30. Omit it to keep the market's current grace period, or 3 days on a market that has none yet. Refused when terms are being switched off.
</ParamField>

<ParamField body="termPenaltyBps" type="integer" default="0">
  Charge on the whole claim when a loan is realized for its term, in basis points. The schema accepts 0 to 50000, but it cannot exceed the market's liquidation penalty. A penalty above zero needs terms.
</ParamField>

<ParamField body="txHash" type="string">
  Hash of the transaction you broadcast for this operation. Send it to confirm the transaction and record the result. Omit it to receive the calldata. When `txHash` is present, every other field is ignored.
</ParamField>

## Switch Terms Off

Send `maxLoanDurationDays: 0` with no `terminalDueDate`, no grace period and no penalty. New loans then open without a maturity, and loans already open keep the maturity they were signed with.

Terms cannot be switched off while the interest model stamps a fixed rate, because the term clock is what pulls a fixed-rate loan toward repayment. Move the rate mode off `FIXED` with [Set Rate Mode](/endpoints/lending/set-rate-mode) first.

A terminal date that has passed closes the market to new loans. Set a future terminal date, or a duration without one, to reopen it.

## Confirm the Change

Send the mined hash back to this path as `{ "txHash": "0x..." }`. The API checks that the transaction called `setTermConfig` on the market. It then reads the offering back from the chain and updates the market record.

## Response Fields

The calldata response carries the transaction twice. `transaction` holds `to` and `data`. The same `to` and `data` also sit directly on `data`, and that is where the `chainId` and `value` binding lands, so sign from those four fields.

<ResponseField name="data" type="object">
  <Expandable>
    <ResponseField name="action" type="string">`SIGN_TRANSACTION`. Returned when `txHash` is omitted.</ResponseField>
    <ResponseField name="transaction" type="object">`{ to, data }` for the `setTermConfig` call on the market. Returned when `txHash` is omitted.</ResponseField>
    <ResponseField name="to" type="string">The market address. Returned when `txHash` is omitted.</ResponseField>
    <ResponseField name="data" type="string">The encoded `setTermConfig` call. Returned when `txHash` is omitted.</ResponseField>
    <ResponseField name="value" type="string">Always `"0"`. Returned when `txHash` is omitted.</ResponseField>
    <ResponseField name="chainId" type="integer">The chain your instance resolves to. Returned when `txHash` is omitted.</ResponseField>
    <ResponseField name="functionName" type="string">`setTermConfig`, or the verified function when confirming.</ResponseField>
    <ResponseField name="requiredRole" type="string">`ISSUER_ROLE`. Returned when `txHash` is omitted.</ResponseField>
    <ResponseField name="termStamping" type="string">`PER_LOAN`, `MARKET_CONFIG` or `UNKNOWN`, as described above. Returned when `txHash` is omitted.</ResponseField>
    <ResponseField name="stampedOnNewLoansOnly" type="array">The term fields this change reaches on new loans only. Returned when `txHash` is omitted.</ResponseField>
    <ResponseField name="appliesToOpenLoans" type="array">The term fields this change also reaches on open loans. Empty on `PER_LOAN`. Returned when `txHash` is omitted.</ResponseField>
    <ResponseField name="openLoansFollowingConfig" type="integer">Open termed loans signed before the market stamped terms, which this change still reaches. Counted over at most 200 recorded positions. Present only on `PER_LOAN` markets.</ResponseField>

    <ResponseField name="warnings" type="array">
      Present only when there is something to say. Each entry carries `kind` and `message`.

      <Expandable>
        <ResponseField name="kind" type="string">`OPEN_TERMED_LOANS_AFFECTED` when the change shortens the grace period or raises the penalty for open loans that follow the live configuration. Carries `openTermedLoans`. `TERM_FLOOR_EXPOSURE` when Dutch auctions are on, the market is not known to run the current implementation, and a term realization sold at the auction floor could leave the pool short. Carries `termPenaltyBps`, `auctionMinPremiumBps` and `uncoveredBps`.</ResponseField>
        <ResponseField name="message" type="string">What the warning means for borrowers or liquidity providers. The floor warning also names the remedies.</ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="description" type="string">Plain-language summary of the change, including which loans it reaches. Returned when `txHash` is omitted.</ResponseField>
    <ResponseField name="requiredSigner" type="string">Always `null` here: the signer is whoever holds `ISSUER_ROLE`, not one fixed address. Returned when `txHash` is omitted.</ResponseField>
    <ResponseField name="heldByThisInstance" type="boolean">`true` when a verified wallet of your instance holds `ISSUER_ROLE`, `false` when none does, `null` when the check could not be completed. The build is never refused on it. Returned when `txHash` is omitted.</ResponseField>
    <ResponseField name="signerAddress" type="string">The wallet of your instance that holds `ISSUER_ROLE`, or `null`. Returned when `txHash` is omitted.</ResponseField>
    <ResponseField name="note" type="string">What to do when your instance cannot sign: the wallet the lender of record placed the role with must sign, because any other wallet reverts `NotAuthorized`. `null` when your instance can sign. Returned when `txHash` is omitted.</ResponseField>
    <ResponseField name="confirmWith" type="object">`{ endpoint, field }`: the call that confirms this transaction, and the body field that carries the hash. Returned when `txHash` is omitted.</ResponseField>
    <ResponseField name="txHash" type="string">Transaction hash. Returned when confirming.</ResponseField>
    <ResponseField name="signedBy" type="string">The wallet that sent the transaction. Returned when confirming.</ResponseField>
    <ResponseField name="blockNumber" type="integer">Block the transaction was mined in. Returned when confirming.</ResponseField>
    <ResponseField name="offering" type="object">The market's rate and term offering, read back from the chain after the transaction, including `maxLoanDuration`, `termGracePeriod`, `terminalDueDate`, `termPenalty` and `marketDueFloor`. The same object [Set Rate Mode](/endpoints/lending/set-rate-mode) returns. Returned when confirming.</ResponseField>
    <ResponseField name="updatedColumns" type="array">Names of the stored market fields this confirmation changed. Returned when confirming.</ResponseField>
  </Expandable>
</ResponseField>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST "https://api.trusset.org/lending-external-securities-v2/api/markets/{marketId}/term-config" \
    -H "X-API-Key: trusset_your_key_here" \
    -H "Content-Type: application/json" \
    -d '{
      "maxLoanDurationDays": 180,
      "termGracePeriodDays": 3,
      "termPenaltyBps": 200
    }'
  ```

  ```typescript TypeScript theme={null}
  const url = `https://api.trusset.org/lending-external-securities-v2/api/markets/${marketId}/term-config`;
  const headers = { 'X-API-Key': 'trusset_your_key_here', 'Content-Type': 'application/json' };

  const build = await fetch(url, {
    method: 'POST',
    headers,
    body: JSON.stringify({ maxLoanDurationDays: 180, termGracePeriodDays: 3, termPenaltyBps: 200 })
  });
  const { data: calldata } = await build.json();
  for (const warning of calldata.warnings ?? []) console.warn(warning.kind, warning.message);

  const tx = await issuerWallet.sendTransaction({
    to: calldata.to,
    data: calldata.data,
    value: calldata.value,
    chainId: calldata.chainId
  });
  await tx.wait();

  await fetch(url, { method: 'POST', headers, body: JSON.stringify({ txHash: tx.hash }) });
  ```
</RequestExample>

<ResponseExample>
  ```json Calldata Response theme={null}
  {
    "success": true,
    "data": {
      "action": "SIGN_TRANSACTION",
      "transaction": {
        "to": "0x70a0e25c7b768b87e658348b3b577678a173e038",
        "data": "0x..."
      },
      "to": "0x70a0e25c7b768b87e658348b3b577678a173e038",
      "data": "0x...",
      "functionName": "setTermConfig",
      "requiredRole": "ISSUER_ROLE",
      "termStamping": "PER_LOAN",
      "stampedOnNewLoansOnly": ["maxLoanDuration", "terminalDueDate", "termGracePeriod", "termPenalty"],
      "appliesToOpenLoans": [],
      "openLoansFollowingConfig": 0,
      "requiredSigner": null,
      "heldByThisInstance": true,
      "signerAddress": "0x5ad87a0621175206b72d10e4b8577b192e7f40ab",
      "note": null,
      "confirmWith": {
        "endpoint": "POST /lending-external-securities-v2/api/markets/clx_secmarket_001/term-config",
        "field": "txHash"
      },
      "chainId": 11155111,
      "value": "0"
    }
  }
  ```

  ```json Error - Fixed Rate Needs Terms theme={null}
  {
    "success": false,
    "error": {
      "code": "TERMS_REQUIRED_BY_FIXED_RATE",
      "message": "Terms cannot be disabled while the model stamps a fixed rate: the term clock is the compensating force for the missing curve. Move the rate mode off FIXED first."
    }
  }
  ```

  ```json Error - Penalty Above Liquidation Penalty theme={null}
  {
    "success": false,
    "error": {
      "code": "INVALID_TERM_OFFERING",
      "message": "The term penalty cannot exceed the liquidation penalty of 500 bps: a borrower who was never undercollateralized owes no larger charge than one who was."
    }
  }
  ```
</ResponseExample>

## Error Codes

| Code                           | HTTP  | Cause                                                                                                                                                  |
| ------------------------------ | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `MISSING_MARKET_ID`            | `400` | The `marketId` path segment is longer than 100 characters                                                                                              |
| `VALIDATION_ERROR`             | `400` | A field is missing, has the wrong type or is outside its range, or `txHash` is malformed                                                               |
| `INVALID_TERM_OFFERING`        | `400` | `terminalDueDate` is unreadable or not in the future, a grace period or penalty was sent without terms, or the penalty exceeds the liquidation penalty |
| `INVALID_CONFIRMATION`         | `400` | The market has no on-chain address, so there is nothing to confirm against                                                                             |
| `MARKET_NOT_FOUND`             | `404` | No market with this ID on your instance                                                                                                                |
| `MARKET_PENDING_CURATOR`       | `409` | The market has no lender of record yet, so nobody holds `ISSUER_ROLE`. Skipped when confirming with `txHash`                                           |
| `TERMS_REQUIRED_BY_FIXED_RATE` | `409` | Terms were being switched off while the interest model stamps a fixed rate                                                                             |
| `INTERNAL_ERROR`               | `500` | The chain could not be read while confirming. Retry shortly                                                                                            |
| `TERM_GRACE_UNRESOLVED`        | `503` | `termGracePeriodDays` was omitted and the market's current grace period could not be read. Send it explicitly, or retry                                |

Confirming with `txHash` can also return any [transaction verification error](/endpoints/introduction#confirm-a-transaction).
