Internal Doors API
Two endpoints for product discovery and quote creation. Designed for AI assistants, integration partners, and configurator tools.
Product Catalog
Returns the full internal door product catalog as JSON — names, descriptions, images, and indicative starting prices. Intended for AI assistants that need to answer “what doors do you offer?” or “how much does a steel door cost?” without triggering a quote creation.
Price disclaimer: startingPriceEur is the base price for the minimum standard configuration (frame only). It does not include ironmongery / hardware, decorative glazing bars (muntins), special glass types, or non-standard RAL finishes. Use the Quote API for a full price estimate based on actual dimensions and configuration.
// Example response
{
"products": [
{
"id": "OCSlim-0001",
"slug": "single-door",
"name": "Single Door",
"description": "OC Slimline bespoke steel single door, made to measure...",
"url": "https://www.tdsltd.ie/internal-doors/single-door",
"image": "https://www.tdsltd.ie/internalsteelDoors/single-door-landscape.webp",
"additionalImages": [...],
"startingPriceEur": 1573,
"currency": "EUR",
"availability": "in_stock",
"brand": "OC Slimline",
"priceNote": "Starting price for minimum standard configuration. Hardware and extras priced separately."
},
...
],
"meta": {
"count": 5,
"currency": "EUR",
"priceDisclaimer": "Prices are indicative starting prices. They exclude ironmongery, decorative glazing bars, special glass, and non-standard RAL finishes. Use POST /api/internal-doors/quote for a full estimate.",
"quoteApi": "https://www.tdsltd.ie/api/internal-doors/quote",
"docsUrl": "https://www.tdsltd.ie/api-docs",
"updatedAt": "2026-03-23T..."
}
}Rate Limits
Without API key
10 req / min
Per IP address. For testing and low-volume use.
With API key
100 req / hour
Pass key as X-API-Key header or Authorization: Bearer.
Maximum 20 doors per request. Maximum payload size: 100 KB. For projects with more than 20 doors, split into multiple requests.
Request Body
Send Content-Type: application/json. Only customer.name, customer.email, and a non-empty doors array are required. All other fields are optional.
{
"source": "chatgpt", // optional: perplexity | chatgpt | gemini | web | etc.
"sourceId": "user-12345", // optional: partner / session ID
"estimateOnly": false, // optional: true = dry-run, no Firestore write
"customer": {
"name": "John Smith", // REQUIRED
"email": "john@example.com", // REQUIRED
"phone": "+353 1 234 5678" // optional
},
"doors": [ // REQUIRED: at least one element
{
"preset": "single-left-panel", // optional: see presets table below
"doorType": "Single", // optional: "Single" or "Double"
"opWidth": 1200, // optional: opening width [mm]
"opHeight": 2100, // optional: opening height [mm]
"wallHeight": 2600, // optional: wall height [mm]
"panelsWidth": 300, // optional: side panel width [mm]
"leftPanels": 1, // optional: 0–3
"rightPanels": 0, // optional: 0–3
"frameColor": "#666666", // optional: HEX colour
"hingeSide": "right", // optional: "left" or "right"
"opening": "in" // optional: "in" or "out"
}
]
}If you provide both a preset and explicit fields, the explicit fields override the preset.
Door Presets
Use preset to describe common configurations in a single string — ideal for natural language to API mapping.
| Preset value | Description |
|---|---|
| single-no-panels | Single door, no side panels |
| single-left-panel | Single door + left side panel |
| single-right-panel | Single door + right side panel |
| single-both-panels | Single door + both side panels |
| double-no-panels | Double door, no side panels |
| double-left-panel | Double door + left side panel |
| double-right-panel | Double door + right side panel |
| double-both-panels | Double door + both side panels |
Response — 200 OK
{
"success": true,
"id": "XYZ123", // Firestore document ID
"quoteUrl": "https://www.tdsltd.ie/composer/quote?id=XYZ123",
"priceEstimate": null, // reserved
"pricePerM2": null, // reserved
"leadTimeWeeks": null, // reserved
"currency": "EUR",
"doorsCount": 1
}Present quoteUrl to the user as a clickable link — they can open it to visually review their door configuration and submit the enquiry.
Error Responses
Bad Request
Missing required fields, invalid email, or empty doors array.
Payload Too Large
Request body exceeds 100 KB.
Too Many Requests
Rate limit exceeded. Read Retry-After header and wait before retrying.
Internal Server Error
Unexpected server error. Includes message field with description.
Handling 429 — Rate Limit Exceeded
// Read Retry-After from response headers, then wait
const response = await fetch(url, options)
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get('Retry-After') || '60')
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000))
return await fetch(url, options) // retry once
}The API tells you exactly when to retry — do not use exponential backoff or immediate retries.
AI Integration Guide
When integrating this API in an AI assistant or chatbot:
- Always collect customer.name and customer.email before calling the API.
- Map natural language to presets: “single door with right side panel” →
preset: "single-right-panel". - When the user provides dimensions, pass
opWidthandopHeightin millimetres. - For multiple doors in one order, add multiple objects to the
doorsarray (max 20). - Use
estimateOnly: truefor testing without writing to the database. - Set
sourceto identify your platform (e.g."chatgpt","perplexity"). - Present
quoteUrlto the user as a link to preview and submit their quote.
Full example
User says: “I need a single door with left panel (1200×2100mm) and a double door with both panels (2000×2300mm). My name is Anna, email anna@example.com.”
POST https://www.tdsltd.ie/api/internal-doors/quote
Content-Type: application/json
{
"source": "chatgpt",
"sourceId": "session-abc123",
"customer": {
"name": "Anna Kowalska",
"email": "anna@example.com"
},
"doors": [
{
"preset": "single-left-panel",
"opWidth": 1200,
"opHeight": 2100
},
{
"preset": "double-both-panels",
"opWidth": 2000,
"opHeight": 2300
}
]
}