Once you have generated a quote and your quote IDs, you must be able to retrieve its details and parse the pricing arrays. This tutorial covers the lifecycle of a quote and provides a technical breakdown of how costs are communicated across all Ethernet product types (Ethernet Internet, E-LAN, and E-Line).
Step 1: Choose your retrieval scenario
Quotes remain available in the system for 30 days from the date of creation. Choose the scenario that matches your needs:
- Scenario A: Retrieve a specific quote by its unique ID.
- Scenario B: List or search for quotes using filters (e.g., status or external reference).
Step 2: Retrieve quote details
Scenario A: Get quote by ID
Use this to pull the full technical and commercial breakdown of a specific quote.
Example request: GET /tmf-api/quoteManagement/v4/quote/{id}
- Example response
-
{
"id": "15582451-1d1e-4049-89dd-797aca356e52",
"@baseType": "Quote",
"@type": "BtwQuote",
"externalId": "",
"href": "https://api.wholesale.bt.com:443/tmf-api/quoteManagement/v4/quote/15582…",
"system": "Dev",
"category": "Wholesale",
"product": "WholesaleEthernetInternet",
"description": "This quote is indicative and non-binding.",
"quoteDate": "2022-02-21T17:03:00.235Z",
"effectiveQuoteCompletionDate": "2022-02-21",
"expectedFulfillmentStartDate": "2022-02-21",
"version": "v1",
"state": "approved",
"validFor": {
"startDateTime": "2022-02-21",
"endDateTime": "2022-02-21"
},
"quoteItem": [
{
"@baseType": "QuoteItem",
"@type": "BtwQuoteItem",
"state": "approved",
"action": "add",
"product": {
"@baseType": "Product",
"@type": "WholesaleEthernetInternet",
"productSpecification": {
"id": "WholesaleEthernetInternet"
},
"place": [
{
"@type": "NadKeySite",
"nadKey": "A00007815344/TH"
}
],
"product": [
{
"@baseType": "Product",
"@type": "EtherwayFibreService",
"productSpecification": {
"id": "EtherwayFibreService"
},
"bandwidth": "1 Gbit/s",
"resilience": "Standard",
"productInformation": {
"accessProvider": "Openreach",
"openreachOnNet": "No",
"servingExchange": {
"code": "THMSD",
"name": "MEDSTEAD"
},
"ethernetSwitch": [
{
"code": "THATN",
"name": "ALTON TE",
"exchangeDistance": {
"unit": "Kilometre",
"value": 8
},
"siteDistance": {
"unit": "Kilometre",
"value": 5
}
}
],
"indicativeECCs": {
"circuitDeliveryCategory": "3",
"tariff": "Unable to calculate ECC",
"indicativeExtraECCs": "Unable to calculate ECC",
"fibrePresent": "No",
"tNodeCapacity": "No"
}
},
"productPrice": [
{
"name": "1 Year connection",
"priceType": "nonRecurring",
"price": {
"taxRate": 20,
"dutyFreeAmount": {
"unit": "GBP",
"value": 1857
},
"taxIncludedAmount": {
"unit": "GBP",
"value": 2228.4
}
}
},
{
"name": "1 Year rental",
"priceType": "recurring",
"recurringChargePeriod": "year",
"price": {
"taxRate": 20,
"dutyFreeAmount": {
"unit": "GBP",
"value": 5431
},
"taxIncludedAmount": {
"unit": "GBP",
"value": 6517.2
}
}
},
{
"name": "3 Year connection",
"priceType": "nonRecurring",
"price": {
"taxRate": 20,
"dutyFreeAmount": {
"unit": "GBP",
"value": 0
},
"taxIncludedAmount": {
"unit": "GBP",
"value": 0
}
}
},
{
"name": "3 Year rental",
"priceType": "recurring",
"recurringChargePeriod": "year",
"price": {
"taxRate": 20,
"dutyFreeAmount": {
"unit": "GBP",
"value": 5431
},
"taxIncludedAmount": {
"unit": "GBP",
"value": 6517.2
}
}
},
{
"name": "5 Year connection",
"priceType": "nonRecurring",
"price": {
"taxRate": 20,
"dutyFreeAmount": {
"unit": "GBP",
"value": 0
},
"taxIncludedAmount": {
"unit": "GBP",
"value": 0
}
}
},
{
"name": "5 Year rental",
"priceType": "recurring",
"recurringChargePeriod": "year",
"price": {
"taxRate": 20,
"dutyFreeAmount": {
"unit": "GBP",
"value": 4166
},
"taxIncludedAmount": {
"unit": "GBP",
"value": 4999.2
}
}
},
{
"name": "3 Year Option B connection",
"priceType": "nonRecurring",
"price": {
"taxRate": 20,
"dutyFreeAmount": {
"unit": "GBP",
"value": 1500
},
"taxIncludedAmount": {
"unit": "GBP",
"value": 1800
}
}
},
{
"name": "3 Year Option B rental",
"priceType": "recurring",
"recurringChargePeriod": "year",
"price": {
"taxRate": 20,
"dutyFreeAmount": {
"unit": "GBP",
"value": 4931
},
"taxIncludedAmount": {
"unit": "GBP",
"value": 5917.2
}
}
}
]
},
{
"@baseType": "Product",
"@type": "EtherflowInternetService",
"productSpecification": {
"id": "EtherflowInternetService"
},
"bandwidth": "30 Mbit/s",
"cos": "Premium CoS",
"ipAddressBlock": "Block /28 (16 LAN IP Addresses)",
"productPrice": [
{
"name": "1 Year connection",
"priceType": "nonRecurring",
"price": {
"taxRate": 20,
"dutyFreeAmount": {
"unit": "GBP",
"value": 0
},
"taxIncludedAmount": {
"unit": "GBP",
"value": 0
}
}
},
{
"name": "1 Year rental",
"priceType": "recurring",
"recurringChargePeriod": "year",
"price": {
"taxRate": 20,
"dutyFreeAmount": {
"unit": "GBP",
"value": 968
},
"taxIncludedAmount": {
"unit": "GBP",
"value": 1161.6
}
}
},
{
"name": "3 Year connection",
"priceType": "nonRecurring",
"price": {
"taxRate": 20,
"dutyFreeAmount": {
"unit": "GBP",
"value": 0
},
"taxIncludedAmount": {
"unit": "GBP",
"value": 0
}
}
},
{
"name": "3 Year rental",
"priceType": "recurring",
"recurringChargePeriod": "year",
"price": {
"taxRate": 20,
"dutyFreeAmount": {
"unit": "GBP",
"value": 968
},
"taxIncludedAmount": {
"unit": "GBP",
"value": 1161.6
}
}
},
{
"name": "5 Year connection",
"priceType": "nonRecurring",
"price": {
"taxRate": 20,
"dutyFreeAmount": {
"unit": "GBP",
"value": 0
},
"taxIncludedAmount": {
"unit": "GBP",
"value": 0
}
}
},
{
"name": "5 Year rental",
"priceType": "recurring",
"recurringChargePeriod": "year",
"price": {
"taxRate": 20,
"dutyFreeAmount": {
"unit": "GBP",
"value": 968
},
"taxIncludedAmount": {
"unit": "GBP",
"value": 1161.6
}
}
},
{
"name": "3 Year Option B connection",
"priceType": "nonRecurring",
"price": {
"taxRate": 20,
"dutyFreeAmount": {
"unit": "GBP",
"value": 0
},
"taxIncludedAmount": {
"unit": "GBP",
"value": 0
}
}
},
{
"name": "3 Year Option B rental",
"priceType": "recurring",
"recurringChargePeriod": "year",
"price": {
"taxRate": 20,
"dutyFreeAmount": {
"unit": "GBP",
"value": 968
},
"taxIncludedAmount": {
"unit": "GBP",
"value": 1161.6
}
}
}
]
}
]
}
}
]
}
Scenario B: List and search quotes
Use query parameters to find quotes created by your organization.
Request parameters
| Property | Example value | Details |
|---|---|---|
| externalId | "MY_REF_123" | Search using your own internal reference ID. |
| fields | "externalId,quoteDate,product" | Use this to limit the response to specific fields, reducing payload size. |
| limit | 10 | Number of records to return per page. |
See the full list of available request parameters in the Reference documentation.
Example request: GET /tmf-api/quoteManagement/v4/quote?fields=externalId,quoteDate,product&externalId=MY_CRM_REF_01
- Example response
-
[
{
"id": "92c3b1f0-06b3-4c4e-9a9f-ae928ceaab4d",
"externalId": "my quote id",
"href": "https://api.wholesale.bt.com:443/tmf-api/quoteManagement/v4/quote/92c3b…",
"product": "WholesaleEthernetElan",
"quoteDate": "2022-02-21T16:12:12.831Z"
},
{
"id": "3f23d132-13f3-487a-8475-b63dff90aa83",
"externalId": "my quote id",
"href": "https://api.wholesale.bt.com:443/tmf-api/quoteManagement/v4/quote/3f23d…",
"product": "WholesaleEthernetEline",
"quoteDate": "2022-02-21T16:33:45.945Z"
},
{
"id": "346437e5-5865-4f1b-a4ae-93a4d28b50c4",
"externalId": "my quote id",
"href": "https://api.wholesale.bt.com:443/tmf-api/quoteManagement/v4/quote/34643…",
"product": "WholesaleEthernetEline",
"quoteDate": "2022-02-21T16:35:08.363Z"
},
{
"id": "a892cbd3-958b-489b-8c3b-8217d16cb9a4",
"externalId": "",
"href": "https://api.wholesale.bt.com:443/tmf-api/quoteManagement/v4/quote/a892c…",
"product": "WholesaleEthernetEline",
"quoteDate": "2022-02-21T16:44:19.629Z"
},
{
"id": "820dec09-3e31-4d1d-934f-334e6fc315d8",
"externalId": "",
"href": "https://api.wholesale.bt.com:443/tmf-api/quoteManagement/v4/quote/820de…",
"product": "WholesaleEthernetElan",
"quoteDate": "2022-02-21T16:46:41.104Z"
},
{
"id": "15582451-1d1e-4049-89dd-797aca356e52",
"externalId": "",
"href": "https://api.wholesale.bt.com:443/tmf-api/quoteManagement/v4/quote/15582…",
"product": "WholesaleEthernetInternet",
"quoteDate": "2022-02-21T17:03:00.235Z"
},
{
"id": "43d7541f-fb18-4f7e-bf53-59d52c9bede4",
"externalId": "my quote id",
"href": "https://api.wholesale.bt.com:443/tmf-api/quoteManagement/v4/quote/43d75…",
"product": "WholesaleInternetConnect",
"quoteDate": "2022-02-21T17:04:05.140Z"
},
{
"id": "346c9400-1582-4633-a1c4-d92f3e32488e",
"externalId": "",
"href": "https://api.wholesale.bt.com:443/tmf-api/quoteManagement/v4/quote/346c9…",
"product": "WholesaleInternetConnect",
"quoteDate": "2022-02-21T17:04:17.987Z"
}
]
Step 4: Interpret the response
Step 4.1: Check the response headers
Before parsing the body, check these headers for environment and rate-limit status:
| Header parameter | Description | Example value |
|---|---|---|
| X-System-Environment | Indicates if the quote is from Sandbox or Production. | test, production |
| X-RateLimit-Remaining | Number of requests remaining in the current minute. | 5 |
| X-RateLimit-Reset | UTC epoch time (seconds) when the limit resets. | 1773319740 |
Step 4.2: Parse the price array and contract terms
The API does not provide a single "total price" field. Instead, it returns an array of productPrice objects. For most Ethernet products, the API returns prices for 12, 36, and 60-month terms simultaneously.
To find the cost for a specific term, filter by priceType AND recurringChargePeriod:
| priceType | Commercial term | Description |
|---|---|---|
| recurring | Monthly/Annual Rental | Charges that repeat. Check recurringChargePeriod (e.g., "12 Months") to see the term-specific rate. |
| nonRecurring | Connection Fee | One-time setup charges. These often decrease as the contract term increases. |
Note on ECCs: Look for price objects where the name mentions "Indicative ECC". These represent estimated Excess Construction Charges for civil works (like digging) and are in addition to standard connection fees.
Step 5: Understand the quote lifecycle
Monitoring the state field is crucial for determining if a quote is ready for ordering.
| State | Description |
|---|---|
| completed | The quote is finished and pricing is available. |
| inProgress | The pricing engine is still calculating (rarely seen in sync calls). |
| rejected | The quote could not be priced (usually due to technical ineligibility). |
| expired | The 30-day validity period has passed. |
Use cases
This section provides practical examples of how to find the information you need by using the API’s filtering options.
When using multiple filters, the API treats these as an AND operation (search will only return quotes that meet both criteria). If you get an empty 200 OK, it’s likely your combined filters are too restrictive.
Use case 1: Building a "recent quotes" dashboard
Goal: To retrieve the most recent 10 quotes to display on a user’s landing page.
- Request: GET /quote?limit=10&sort=-validQuoteUntil
- What to look for: Use the limit parameter to prevent huge payloads. Sorting by validQuoteUntil (descending, indicated by the -) ensures the user sees their newest, most relevant quotes first.
Use case 2: Synchronizing quotes within a specific timeframe
Goal: To fetch all quotes created during a specific sales campaign or financial week.
- Request: GET /quote?quoteDate.gt=2026-03-01T00:00:00Z"eDate.lt=2026-03-07T23:59:59Z
- What to look for: Notice the .gt (greater than) and .lt (less than) modifiers. This is standard TMF filtering logic. Ensure your timestamps are in ISO 8601 format.
Use case 3: Categorizing by product type
Goal: To filter the history to show only E-Line quotes, excluding Internet or LAN.
- Request: GET /quote?category=WholesaleEthernetEline
- What to look for: The category or product.name filter is essential if your organization manages multiple product lines. This prevents your E-Line provisioning team from seeing unrelated Internet-access quotes.
Use case 4: Handling pagination for large datasets
Goal: To let a user browse through their entire quote history using "Next/Previous" buttons.
- Request (Page 2): GET /quote?limit=20&offset=20
- What to look for: The offset parameter tells the API how many records to skip. To calculate your total pages, always look at the X-Total-Count response header from your first request; it tells you exactly how many records exist in the database versus the small slice you requested.
Need help?
Browse the Common API-specific errors & Troubleshooting reference table.