API Reference

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 priceableAttributes is omitted, catalog dimensions (all types) are copied; prices are not.
  • GET rate cards with empty content means no exception (exceptionPricing: false).
  • DELETE an exception to return to catalog rates.

Endpoint summary

MethodPath
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
QueryDescription
page0-based page index (default 0)
sizePage size (default 20, max 20)
qOptional search on name, number

Response 200ApiResponse 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_TOKEN

Returns 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_TOKEN

number 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_TOKEN

Updates 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_TOKEN

Response 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_TOKEN

Response 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_TOKEN

Returns 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_TOKEN

serviceId 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_TOKEN

Request

{
  "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_TOKEN

End 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_TOKEN

Request

{
  "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_TOKEN

Response 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_TOKEN

Response 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_TOKEN

Response 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_TOKEN

Request

{
  "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_TOKEN

Request

{
  "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_TOKEN

Response 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_TOKEN

An 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_TOKEN

Response 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_TOKEN

Response 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_TOKEN

Returns 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_TOKEN

The 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_TOKEN

Drops the exception; rating falls back to the catalog service rate card.

Response 204 — no body.