> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.tabs.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.tabs.com/_mcp/server.

# Update commitment schedule

PATCH https://integrators.prod.api.tabsplatform.com/v3/contracts/{id}/commitments/{commitmentId}/schedules/{scheduleId}
Content-Type: application/json

Reference: https://docs.tabs.com/api-reference/contracts/integrators-api-contractsv-3-controller-patch-commitment-schedule

## Authentication

- `Authorization` header (required)

## Request

### Path parameters

- `id` (string, required) — Contract Id
- `commitmentId` (string, required) — Commitment Id
- `scheduleId` (string, required) — Schedule Id

### Body (application/json)

- `cascadeBillingTerms` (boolean, required) — Required. When true, extend or shorten step-linked base BTs (and their POBs) to match the new schedule endDate, plus resync container BT dates. When false, the schedule mutates alone and the caller is responsible for BT realignment.
- `endDate` (string, optional) — New schedule end date (YYYY-MM-DD, inclusive). Only valid on the LAST schedule of the commitment (changes on earlier schedules would break contiguity). Must be ≥ the end of the latest commitment period on this schedule that has a non-voided invoice, and must align with a period boundary of every linked base BT's billing cadence. When composed with `startDateChange` on a single-schedule commitment, this value wins as the final absolute endDate (applied after startDateChange).
- `startDateChange` (object, optional) — Optional start-date mutation. When present, only valid if the URL scheduleId is the FIRST schedule of the commitment (min sequence). `mode: extend` mutates only that schedule's startDate; blocked if it would cross a period that has a non-voided invoice. `mode: shift` translates every schedule and every commitment period by the delta; blocked entirely if any non-voided invoice exists anywhere on the commitment. When `cascadeBillingTerms=false`, both modes must align with linked base BT billing-period boundaries (the BT grid stays put); when `cascadeBillingTerms=true`, the BT grid re-anchors to the schedule's new start, so alignment against the old anchor is not enforced. With cascade: `extend` moves linked base BTs' startDate + attached POBs' serviceStartDate to match the schedule's new start; `shift` translates every linked base BT and container BT (both edges) and every attached POB (both service edges) by the same calendar delta.
  - `mode` (enum, required) — `extend` mutates only the first schedule's startDate (moves it earlier, or shortens it up to — but not past — the first sent invoice). `shift` translates every schedule (and all commitment periods) by the delta between the current first-schedule startDate and the new startDate; blocked entirely if any invoice has been sent anywhere on the commitment.
    - Allowed values: `extend`, `shift`
  - `startDate` (string, required) — New startDate (YYYY-MM-DD, inclusive) for the first schedule.
- `commitmentInterval` (enum, optional) — New cadence for the schedule's commitment periods. Rejected if any period on this schedule already has a non-voided invoice, or if the schedule is multi-step (invariant #7: multi-step schedules must be full-duration).
  - Allowed values: `DAILY`, `WEEKLY`, `MONTHLY`, `QUARTERLY`, `YEARLY`
- `prepaymentScheduleType` (enum, optional) — New prepayment schedule type. Rejected if any period on this schedule already has a non-voided invoice.
  - Allowed values: `FULL_UPFRONT`, `PER_COMMITMENT_PERIOD`, `SPLIT_ACROSS_BILLING_PERIODS`
- `billingTermsToAdd` (list of map from string to list of string, optional) — Per-step billing term additions. Each element is \{ \[stepId]: btId\[] }. All steps must end with the same event-type set (invariant #6). Only base BT links (billingTermCommitmentType=null) can be added here.
- `billingTermsToDelete` (list of map from string to list of string, optional) — Per-step billing term removals. Each element is \{ \[stepId]: btId\[] }. The BT must currently be linked as a base BT on that step.

## Response

### 200

Schedule patched

- `payload` (object, required) — Response payload, will be empty when success is false
  - `scheduleId` (string, required) — ID of the patched schedule
- `success` (boolean, required) — Boolean with true=success, false=failure
- `message` (string, required) — Plain-text description of the result
- `error` (object, optional) — json element with any error messages or warnings
  - `code` (double, required) — API response code
  - `message` (string, required) — API response message
  - `details` (object, optional) — Additional details about the error

## Examples

**Request**

```json
{
  "cascadeBillingTerms": false
}
```

**Response**

```json
{
  "payload": {
    "scheduleId": "string"
  },
  "success": true,
  "message": "string",
  "error": {
    "code": 1.1,
    "message": "string",
    "details": {}
  }
}
```

**SDK Code**

```python
import requests

url = "https://integrators.prod.api.tabsplatform.com/v3/contracts/123e4567-e89b-12d3-a456-426614174000/commitments/commitmentId/schedules/scheduleId"

payload = { "cascadeBillingTerms": False }
headers = {
    "Authorization": "<apiKey>",
    "Content-Type": "application/json"
}

response = requests.patch(url, json=payload, headers=headers)

print(response.json())
```

```javascript
const url = 'https://integrators.prod.api.tabsplatform.com/v3/contracts/123e4567-e89b-12d3-a456-426614174000/commitments/commitmentId/schedules/scheduleId';
const options = {
  method: 'PATCH',
  headers: {Authorization: '<apiKey>', 'Content-Type': 'application/json'},
  body: '{"cascadeBillingTerms":false}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://integrators.prod.api.tabsplatform.com/v3/contracts/123e4567-e89b-12d3-a456-426614174000/commitments/commitmentId/schedules/scheduleId"

	payload := strings.NewReader("{\n  \"cascadeBillingTerms\": false\n}")

	req, _ := http.NewRequest("PATCH", url, payload)

	req.Header.Add("Authorization", "<apiKey>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://integrators.prod.api.tabsplatform.com/v3/contracts/123e4567-e89b-12d3-a456-426614174000/commitments/commitmentId/schedules/scheduleId")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Patch.new(url)
request["Authorization"] = '<apiKey>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"cascadeBillingTerms\": false\n}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.patch("https://integrators.prod.api.tabsplatform.com/v3/contracts/123e4567-e89b-12d3-a456-426614174000/commitments/commitmentId/schedules/scheduleId")
  .header("Authorization", "<apiKey>")
  .header("Content-Type", "application/json")
  .body("{\n  \"cascadeBillingTerms\": false\n}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('PATCH', 'https://integrators.prod.api.tabsplatform.com/v3/contracts/123e4567-e89b-12d3-a456-426614174000/commitments/commitmentId/schedules/scheduleId', [
  'body' => '{
  "cascadeBillingTerms": false
}',
  'headers' => [
    'Authorization' => '<apiKey>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://integrators.prod.api.tabsplatform.com/v3/contracts/123e4567-e89b-12d3-a456-426614174000/commitments/commitmentId/schedules/scheduleId");
var request = new RestRequest(Method.PATCH);
request.AddHeader("Authorization", "<apiKey>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"cascadeBillingTerms\": false\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "Authorization": "<apiKey>",
  "Content-Type": "application/json"
]
let parameters = ["cascadeBillingTerms": false] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://integrators.prod.api.tabsplatform.com/v3/contracts/123e4567-e89b-12d3-a456-426614174000/commitments/commitmentId/schedules/scheduleId")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "PATCH"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```