Contracts
External CRUD for contracts, contract services, and optional exception rate cards.
Contracts
Prefix: /api/v1/external/contracts
See Overview for authentication, pagination, and errors. Rate-card payload variants are documented on Services — Rate card write body.
Exception pricing
A contract-service rate card is optional exception pricing for that line. Rating already falls back to the catalog service rate card when no exception exists.
- Omit
rateCards(or send[]) when adding a service — no catalog matrix is copied. - If
priceableAttributesis omitted, catalog dimensions (all types) are copied; prices are not. - GET rate cards with empty
contentmeans no exception (exceptionPricing: false). - DELETE an exception to return to catalog rates.
Endpoint summary
| Method | Path |
|---|---|
| GET | /contracts |
| GET | /contracts/{contractId} |
| POST | /contracts |
| PUT | /contracts/{contractId} |
| DELETE | /contracts/{contractId} |
| GET | /contract-services/{contractServiceId} |
| PUT | /contract-services/{contractServiceId} |
| PUT | /contract-services/{contractServiceId}/end |
| PUT | /contracts/services/by-instance/{serviceInstanceId}/end |
| GET | /contracts/{contractId}/services |
| GET | /contracts/{contractId}/services/{contractServiceId} |
| POST | /contracts/{contractId}/services |
| PUT | /contracts/{contractId}/services/{contractServiceId} |
| PUT | /contracts/{contractId}/services/{contractServiceId}/end |
| DELETE | /contracts/{contractId}/services/{contractServiceId} |
| GET | /contracts/{contractId}/services/{contractServiceId}/priceable-attributes |
| GET | /contracts/{contractId}/services/{contractServiceId}/priceable-attributes/{attributeId} |
| POST | /contracts/{contractId}/services/{contractServiceId}/priceable-attributes |
| PUT | /contracts/{contractId}/services/{contractServiceId}/priceable-attributes/{attributeId} |
| DELETE | /contracts/{contractId}/services/{contractServiceId}/priceable-attributes/{attributeId} |
| GET | /contracts/{contractId}/services/{contractServiceId}/rate-cards |
| GET | /contracts/{contractId}/services/{contractServiceId}/rate-cards/{currency} |
| GET | /contracts/{contractId}/services/{contractServiceId}/rate-cards/{currency}/versions |
| POST | /contracts/{contractId}/services/{contractServiceId}/rate-cards |
| PUT | /contracts/{contractId}/services/{contractServiceId}/rate-cards/{currency} |
| DELETE | /contracts/{contractId}/services/{contractServiceId}/rate-cards/{currency} |
Contract CRUD
List contracts
GET /api/v1/external/contracts?page=0&size=20&q=acme
Authorization: Bearer YOUR_API_TOKEN| Query | Description |
|---|---|
page | 0-based page index (default 0) |
size | Page size (default 20, max 20) |
q | Optional search on name, number |
Response 200 — ApiResponse wrapping a Spring Page (see Overview).
{
"requestId": "req-1",
"success": true,
"status": 200,
"errors": [],
"data": {
"content": [
{
"__type__": "Contract",
"Id": "3a2f1c5d-9b7e-4c1a-8f36-2d4b6e8a0c11",
"Name": "Acme MSA",
"Number": "C-1001",
"ApprovalStatus": "fe07e71d-cba4-4316-9c7c-4935c3adec34",
"ApprovalStatus__r": { "Name": "Approved" },
"MasterAccountId": "11111111-1111-1111-1111-111111111111"
}
],
"totalElements": 1,
"size": 20,
"number": 0
}
}Get contract
GET /api/v1/external/contracts/{contractId}
Authorization: Bearer YOUR_API_TOKENReturns the contract header row (__r shape). Nested service summaries are not expanded — list /contracts/{id}/services or GET /contract-services/{id}.
POST /contracts accepts accountExtId when masterAccountId is omitted, templateId on nested services as serviceId, optional dimensionValues / accountMap, and generates number (C-{timestamp}) if omitted.
End a service with PUT /contract-services/{contractServiceId}/end, PUT .../contracts/{contractId}/services/{contractServiceId}/end, or PUT /contracts/services/by-instance/{serviceInstanceId}/end (409 if the instance id is not unique). Body: { "terminationDate": "2026-09-01", "terminationMessage": "..." }. Do not use DELETE to end a service.
Response 200
{
"id": "3a2f1c5d-9b7e-4c1a-8f36-2d4b6e8a0c11",
"name": "Acme MSA",
"number": "C-1001",
"startDate": "2026-01-01",
"endDate": "2026-12-31",
"currency": "USD",
"masterAccountId": "11111111-1111-1111-1111-111111111111",
"approvalStatus": "Approved",
"services": [
{
"id": "7c9d2e41-5b83-4f6a-9e21-0a5c7d3b8f42",
"serviceId": "e6cbd28a-f34c-482e-8da5-34e21dbd5eb7",
"serviceInstanceId": "SI-1",
"startDate": "2026-01-01",
"endDate": "2026-12-31",
"exceptionPricing": false
}
]
}Create contract
POST /api/v1/external/contracts
Content-Type: application/json
Authorization: Bearer YOUR_API_TOKENnumber is required and must be unique (409 otherwise). services is optional.
Request
{
"name": "Acme MSA",
"number": "C-1001",
"startDate": "2026-01-01",
"endDate": "2026-12-31",
"currency": "USD",
"masterAccountId": "11111111-1111-1111-1111-111111111111",
"poNumber": "PO-88213",
"totalPrice": 120000,
"services": [
{
"serviceId": "e6cbd28a-f34c-482e-8da5-34e21dbd5eb7",
"serviceInstanceId": "SI-1",
"startDate": "2026-01-01",
"endDate": "2026-12-31"
}
]
}Response 201 — each nested service without rateCards has exceptionPricing: false.
{
"id": "3a2f1c5d-9b7e-4c1a-8f36-2d4b6e8a0c11",
"name": "Acme MSA",
"number": "C-1001",
"startDate": "2026-01-01",
"endDate": "2026-12-31",
"currency": "USD",
"masterAccountId": "11111111-1111-1111-1111-111111111111",
"poNumber": "PO-88213",
"totalPrice": 120000,
"services": [
{
"id": "7c9d2e41-5b83-4f6a-9e21-0a5c7d3b8f42",
"serviceId": "e6cbd28a-f34c-482e-8da5-34e21dbd5eb7",
"serviceInstanceId": "SI-1",
"startDate": "2026-01-01",
"endDate": "2026-12-31",
"exceptionPricing": false
}
]
}Update contract
PUT /api/v1/external/contracts/{contractId}
Content-Type: application/json
Authorization: Bearer YOUR_API_TOKENUpdates scalars. If services is present, existing contract services are replaced. Omit services to leave lines unchanged.
Request
{
"name": "Acme MSA (2026 renewal)",
"endDate": "2027-12-31",
"totalPrice": 145000
}Response 200 — the full contract detail, identical in shape to Get contract.
Delete contract
DELETE /api/v1/external/contracts/{contractId}
Authorization: Bearer YOUR_API_TOKENResponse 204 — no body.
Response 409 if the contract still has services.
{
"status": 409,
"error": "Conflict",
"message": "Contract still has services",
"path": "uri=/api/v1/external/contracts/3a2f1c5d-9b7e-4c1a-8f36-2d4b6e8a0c11"
}Contract services
List contract services
GET /api/v1/external/contracts/{contractId}/services?page=0&size=20
Authorization: Bearer YOUR_API_TOKENResponse 200
{
"content": [
{
"id": "7c9d2e41-5b83-4f6a-9e21-0a5c7d3b8f42",
"serviceId": "e6cbd28a-f34c-482e-8da5-34e21dbd5eb7",
"serviceInstanceId": "SI-1",
"startDate": "2026-01-01",
"endDate": "2026-12-31",
"chargeFrequency": "Monthly",
"billingFrequency": "Monthly",
"exceptionPricing": true
}
],
"page": 0,
"size": 20,
"totalElements": 1,
"totalPages": 1
}Get contract service
GET /api/v1/external/contracts/{contractId}/services/{contractServiceId}
Authorization: Bearer YOUR_API_TOKENReturns the line plus its priceable attributes and exception rateCards (empty array when no exception).
Response 200
{
"id": "7c9d2e41-5b83-4f6a-9e21-0a5c7d3b8f42",
"serviceId": "e6cbd28a-f34c-482e-8da5-34e21dbd5eb7",
"serviceInstanceId": "SI-1",
"startDate": "2026-01-01",
"endDate": "2026-12-31",
"chargeFrequency": "Monthly",
"billingFrequency": "Monthly",
"exceptionPricing": true,
"priceableAttributes": [
{
"id": "5c1e7a29-3f8b-4d62-90ac-4b6d2f8e1c53",
"name": "Input_Tokens",
"label": "Input Tokens",
"dimensionDataType": "Number",
"dimValues": []
}
],
"rateCards": [
{
"currency": "USD",
"versionNo": 1,
"isCurrent": true,
"fromDate": "2026-01-01",
"rows": [
{
"id": "6a3c9e17-8d2b-4f50-a91e-7c5b0d4f2a68",
"priceDimId": "5c1e7a29-3f8b-4d62-90ac-4b6d2f8e1c53",
"label": "Negotiated input token rate",
"priceMethod": "QUANTITY",
"unitRate": 0.0018,
"attributeValues": []
}
]
}
]
}Add a service to a contract
POST /api/v1/external/contracts/{contractId}/services
Content-Type: application/json
Authorization: Bearer YOUR_API_TOKENserviceId is required. Omit priceableAttributes to copy catalog dimensions; omit rateCards to keep catalog list prices.
Request — attach at catalog pricing.
{
"serviceId": "e6cbd28a-f34c-482e-8da5-34e21dbd5eb7",
"serviceInstanceId": "SI-1",
"startDate": "2026-01-01",
"endDate": "2026-12-31",
"chargeFrequency": "Monthly",
"billingFrequency": "Monthly"
}Request — attach with exception pricing in the same call.
{
"serviceId": "e6cbd28a-f34c-482e-8da5-34e21dbd5eb7",
"serviceInstanceId": "SI-2",
"startDate": "2026-01-01",
"endDate": "2026-12-31",
"rateCards": [
{
"currency": "USD",
"rows": [
{
"priceMethod": "QUANTITY",
"priceDimName": "Input_Tokens",
"label": "Negotiated input token rate",
"unitRate": 0.0018,
"attributeValues": []
}
]
}
]
}Response 201 — the full contract service, identical in shape to Get contract service.
Update contract service
PUT /api/v1/external/contracts/{contractId}/services/{contractServiceId}
Content-Type: application/json
Authorization: Bearer YOUR_API_TOKENRequest
{
"endDate": "2027-06-30",
"billingFrequency": "Quarterly"
}Response 200 — the full contract service, identical in shape to Get contract service.
The same update (no contract id in the path):
PUT /api/v1/external/contract-services/{contractServiceId}
Content-Type: application/json
Authorization: Bearer YOUR_API_TOKENEnd contract service
PUT /api/v1/external/contract-services/{contractServiceId}/end
PUT /api/v1/external/contracts/{contractId}/services/{contractServiceId}/end
Content-Type: application/json
Authorization: Bearer YOUR_API_TOKENRequest
{
"terminationDate": "2026-09-01",
"terminationMessage": "Offboarded"
}Response 200 — the ended contract-service row (ApiResponse + __r shape).
Delete contract service
DELETE /api/v1/external/contracts/{contractId}/services/{contractServiceId}
Authorization: Bearer YOUR_API_TOKENResponse 204 — no body.
Priceable attributes (contract service)
Same body shape as catalog service attributes, plus the optional dimensionValText field.
List contract-service priceable attributes
GET /api/v1/external/contracts/{contractId}/services/{contractServiceId}/priceable-attributes?page=0&size=20
Authorization: Bearer YOUR_API_TOKENResponse 200
{
"content": [
{
"id": "5c1e7a29-3f8b-4d62-90ac-4b6d2f8e1c53",
"name": "Input_Tokens",
"label": "Input Tokens",
"dimensionDataType": "Number",
"isContractInput": true,
"dimValues": []
}
],
"page": 0,
"size": 20,
"totalElements": 1,
"totalPages": 1
}Get contract-service priceable attribute
GET /api/v1/external/contracts/{contractId}/services/{contractServiceId}/priceable-attributes/{attributeId}
Authorization: Bearer YOUR_API_TOKENResponse 200
{
"id": "5c1e7a29-3f8b-4d62-90ac-4b6d2f8e1c53",
"name": "Input_Tokens",
"label": "Input Tokens",
"dimensionDataType": "Number",
"isContractInput": true,
"dimensionValText": "1000",
"dimValues": []
}Create contract-service priceable attribute
POST /api/v1/external/contracts/{contractId}/services/{contractServiceId}/priceable-attributes
Content-Type: application/json
Authorization: Bearer YOUR_API_TOKENRequest
{
"name": "Committed_Tokens",
"label": "Committed Tokens",
"dimensionDataType": "Number",
"isContractInput": true,
"dimensionValText": "5000000"
}Response 201
{
"id": "8b4f0d36-9a1c-4e75-b2d8-6f3a5c9e0b21",
"name": "Committed_Tokens",
"label": "Committed Tokens",
"dimensionDataType": "Number",
"isContractInput": true,
"dimensionValText": "5000000",
"dimValues": []
}Update contract-service priceable attribute
PUT /api/v1/external/contracts/{contractId}/services/{contractServiceId}/priceable-attributes/{attributeId}
Content-Type: application/json
Authorization: Bearer YOUR_API_TOKENRequest
{
"name": "Committed_Tokens",
"label": "Committed Tokens (revised)",
"dimensionDataType": "Number",
"dimensionValText": "7500000"
}Response 200
{
"id": "8b4f0d36-9a1c-4e75-b2d8-6f3a5c9e0b21",
"name": "Committed_Tokens",
"label": "Committed Tokens (revised)",
"dimensionDataType": "Number",
"dimensionValText": "7500000",
"dimValues": []
}Delete contract-service priceable attribute
DELETE /api/v1/external/contracts/{contractId}/services/{contractServiceId}/priceable-attributes/{attributeId}
Authorization: Bearer YOUR_API_TOKENResponse 204 — no body.
Response 409 if a current exception rate-card row references the attribute.
Exception rate cards
Same body shape as catalog rate cards. See Rate card write body for every priceMethod variant.
List exception rate cards
GET /api/v1/external/contracts/{contractId}/services/{contractServiceId}/rate-cards?page=0&size=20
Authorization: Bearer YOUR_API_TOKENAn empty content array means there is no exception and rating uses catalog list price.
Response 200
{
"content": [
{
"currency": "USD",
"versionNo": 1,
"isCurrent": true,
"fromDate": "2026-01-01",
"rows": [
{
"id": "6a3c9e17-8d2b-4f50-a91e-7c5b0d4f2a68",
"priceDimId": "5c1e7a29-3f8b-4d62-90ac-4b6d2f8e1c53",
"label": "Contract override",
"priceMethod": "FLAT_RATE",
"flatRate": 75.0,
"attributeValues": []
}
]
}
],
"page": 0,
"size": 20,
"totalElements": 1,
"totalPages": 1
}Get exception rate card by currency
GET /api/v1/external/contracts/{contractId}/services/{contractServiceId}/rate-cards/USD
Authorization: Bearer YOUR_API_TOKENResponse 200
{
"currency": "USD",
"versionNo": 1,
"isCurrent": true,
"fromDate": "2026-01-01",
"rows": [
{
"id": "6a3c9e17-8d2b-4f50-a91e-7c5b0d4f2a68",
"priceDimId": "5c1e7a29-3f8b-4d62-90ac-4b6d2f8e1c53",
"label": "Contract override",
"priceMethod": "FLAT_RATE",
"flatRate": 75.0,
"attributeValues": []
}
]
}Returns 404 when there is no exception for that currency.
List exception rate-card versions
GET /api/v1/external/contracts/{contractId}/services/{contractServiceId}/rate-cards/USD/versions?page=0&size=20
Authorization: Bearer YOUR_API_TOKENResponse 200
{
"content": [
{
"currency": "USD",
"versionNo": 2,
"isCurrent": true,
"fromDate": "2026-07-01",
"rows": [
{ "priceDimId": "5c1e7a29-3f8b-4d62-90ac-4b6d2f8e1c53", "label": "Contract override", "priceMethod": "FLAT_RATE", "flatRate": 70.0, "attributeValues": [] }
]
},
{
"currency": "USD",
"versionNo": 1,
"isCurrent": false,
"fromDate": "2026-01-01",
"toDate": "2026-06-30",
"rows": [
{ "priceDimId": "5c1e7a29-3f8b-4d62-90ac-4b6d2f8e1c53", "label": "Contract override", "priceMethod": "FLAT_RATE", "flatRate": 75.0, "attributeValues": [] }
]
}
],
"page": 0,
"size": 20,
"totalElements": 2,
"totalPages": 1
}Set exception pricing
POST /api/v1/external/contracts/{contractId}/services/{contractServiceId}/rate-cards
Content-Type: application/json
Authorization: Bearer YOUR_API_TOKENReturns 409 if that currency already has an exception card.
Request
{
"currency": "USD",
"rows": [
{
"priceMethod": "FLAT_RATE",
"priceDimName": "Usage",
"label": "Contract override",
"flatRate": 75.0,
"attributeValues": []
}
]
}curl -sS -X POST \
"https://YOUR_TENANT_HOST/api/v1/external/contracts/3a2f1c5d-9b7e-4c1a-8f36-2d4b6e8a0c11/services/7c9d2e41-5b83-4f6a-9e21-0a5c7d3b8f42/rate-cards" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"currency": "USD",
"rows": [{
"priceMethod": "FLAT_RATE",
"priceDimName": "Usage",
"label": "Contract override",
"flatRate": 75.0,
"attributeValues": []
}]
}'Response 201
{
"currency": "USD",
"versionNo": 1,
"isCurrent": true,
"fromDate": "2026-08-31",
"rows": [
{
"id": "6a3c9e17-8d2b-4f50-a91e-7c5b0d4f2a68",
"priceDimId": "5c1e7a29-3f8b-4d62-90ac-4b6d2f8e1c53",
"label": "Contract override",
"priceMethod": "FLAT_RATE",
"flatRate": 75.0,
"attributeValues": []
}
]
}Update exception pricing
PUT /api/v1/external/contracts/{contractId}/services/{contractServiceId}/rate-cards/USD
Content-Type: application/json
Authorization: Bearer YOUR_API_TOKENThe currency in the body must match the path segment.
Request
{
"currency": "USD",
"rows": [
{
"priceMethod": "FLAT_RATE",
"priceDimName": "Usage",
"label": "Contract override",
"flatRate": 70.0,
"attributeValues": []
}
]
}Response 200
{
"currency": "USD",
"versionNo": 2,
"isCurrent": true,
"fromDate": "2026-08-31",
"rows": [
{
"id": "9c5e1a48-0b7d-4386-c3f1-8a2d6e0b4f79",
"priceDimId": "5c1e7a29-3f8b-4d62-90ac-4b6d2f8e1c53",
"label": "Contract override",
"priceMethod": "FLAT_RATE",
"flatRate": 70.0,
"attributeValues": []
}
]
}Remove exception pricing
DELETE /api/v1/external/contracts/{contractId}/services/{contractServiceId}/rate-cards/USD
Authorization: Bearer YOUR_API_TOKENDrops the exception; rating falls back to the catalog service rate card.
Response 204 — no body.