Complete Tender Integration Platform

Access live tenders from across Africa. Display, filter, and integrate tender data seamlessly into any application.

Security Notice: Domain locking restricts API usage to registered websites only. Testing from this documentation page is automatically allowed.

Real-time Data

Access live tender data updated continuously from multiple African countries

Advanced Filtering

Filter tenders by country, category, sector, county, and closing date

Secure Document Access

Download tender documents securely through our API gateway

Secure & Reliable

Enterprise-grade API with rate limiting and 99.9% uptime guarantee

Get Started in 5 Minutes

Important: Your API key is domain-locked. It works automatically from:
  • Your registered website(s)
  • api.tendersoko.africa (for testing)
  • localhost (for development)
1

Get Your API Key

Register for a free account and get your API key from the dashboard.

Get API Key
2

Learn Secure Authentication

Use HTTP headers for API authentication instead of query parameters.

// ✅ SECURE: Use headers headers: { 'Authorization': 'Bearer YOUR_API_KEY' }
3

Choose Integration Method

Select from our pre-built solutions or build your own integration.

4

Go Live

Deploy your integration and start displaying live tenders to your users.

Your integration is ready for production use

Quick Authentication Test

Enter your API key to test connectivity and explore the API:

Enter your API key to test the connection

API Testing Playground

🔑 API Authentication

Enter your API key to start testing

🔍 Professional Test Endpoints

Document Download Tests

These endpoints trigger file downloads directly. Use the iframe method for reliable downloads.

API Response
// Response will appear here...

Complete API Endpoints Reference

Security Notice: All API requests must include authentication via HTTP headers. Query parameters are deprecated but supported for backward compatibility.

📊 Tender Management Endpoints

GET
/api/v1/countries

Get all available countries with their codes

GET
/api/v1/tenders

Get all active tenders with optional filtering

?country=KE&category=5§or=12
GET
/api/v1/tenders/{id}

Get detailed information about a specific tender

/api/v1/tenders/1

Tender descriptions are automatically formatted and sanitized

GET
/api/v1/today

Get tenders published today

GET
/api/v1/closing

Get tenders closing soon (default: 7 days)

?days=3&country=KE

📁 Document Management Endpoints

GET
/api/v1/tenders/{id}/documents

Get all documents for a specific tender

/api/v1/tenders/1/documents

Returns: { "status": true, "documents": [...] }

GET
/api/v1/advert_doc/{id}

Download tender advertisement as PDF (Direct Download)

/api/v1/advert_doc/1

Auto-downloads: tender-title-advert.pdf

Note: This is a binary download, not a JSON response

GET
/api/v1/documents/{id}/download

Download specific document (Direct Download)

/api/v1/documents/1/download

Auto-downloads: document-title.extension

Note: This is a binary download, not a JSON response

📊 Reference Data Endpoints

GET
/api/v1/categories

Get all tender categories

GET
/api/v1/sectors

Get all tender sectors

GET
/api/v1/statuses

Get all tender statuses

GET
/api/v1/counties

Get all counties (optionally filter by country_id)

?country_id=1

🏢 Company Information Endpoints

GET
/api/v1/company_logo/{company_id}

Get company logo information by company ID

/api/v1/company_logo/1

Returns logo URL and whether it's the default placeholder

{ "status": true, "logo_url": "https://www.tendersoko.com/images/companies/logo.png", "is_default": false }

📁 Document Download Implementation

Document Download Implementation
// Tendersoko API - Document Download Solution
const API_KEY = 'YOUR_API_KEY_HERE';
const API_BASE = 'https://api.tendersoko.africa/api/v1';

/**
 * Download tender advert - uses hidden iframe method
 * This method avoids console errors and works reliably
 */
function downloadAdvert(tenderId) {
    const iframe = document.createElement('iframe');
    iframe.style.display = 'none';
    iframe.src = `${API_BASE}/advert_doc/${tenderId}`;
    document.body.appendChild(iframe);
    
    setTimeout(() => {
        if (iframe.parentNode) {
            document.body.removeChild(iframe);
        }
    }, 5000);
    
    return true;
}

/**
 * Download document - works for all document types (PDF, DOC, etc.)
 */
function downloadDocument(docId) {
    const iframe = document.createElement('iframe');
    iframe.style.display = 'none';
    iframe.src = `${API_BASE}/documents/${docId}/download`;
    document.body.appendChild(iframe);
    
    setTimeout(() => {
        if (iframe.parentNode) {
            document.body.removeChild(iframe);
        }
    }, 5000);
    
    return true;
}

/**
 * Alternative method using fetch (may show console errors but works)
 */
async function downloadDocumentWithFetch(docId, docTitle, docExtension) {
    try {
        const response = await fetch(`${API_BASE}/documents/${docId}/download`, {
            headers: {
                'Authorization': 'Bearer ' + API_KEY
            }
        });
        
        if (!response.ok) {
            throw new Error(`HTTP ${response.status}`);
        }
        
        const blob = await response.blob();
        const url = window.URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        
        // Create safe filename
        const safeTitle = docTitle
            .replace(/[^a-zA-Z0-9\s-]/g, '')
            .replace(/\s+/g, '-')
            .toLowerCase()
            .substring(0, 100);
        
        a.download = `${safeTitle}.${docExtension.toLowerCase()}`;
        document.body.appendChild(a);
        a.click();
        window.URL.revokeObjectURL(url);
        document.body.removeChild(a);
        
        return { success: true };
    } catch (error) {
        console.warn('Fetch method failed, falling back to iframe');
        downloadDocument(docId);
        return { success: true, method: 'fallback' };
    }
}

// Example usage:
// downloadAdvert(1); // Downloads PDF advert
// downloadDocument(1); // Downloads specific document

🔧 Quick Integration Examples

Example
Download Tender Advert
downloadAdvert(1)

Downloads: tender-advert.pdf

Example
List Tender Documents
/api/v1/tenders/1/documents

Returns JSON list of documents for tender 1

Complete Integration Guide

Security Best Practices: Always use HTTP headers for API authentication. Never expose API keys in client-side code or URLs.

🚀 Quick Start - Basic Tender Display

Copy and paste this code to display tenders on your website:

Basic HTML Integration (Secure)
<!DOCTYPE html>
<html>
<head>
    <title>Tenders Display</title>
    <style>
        .tenders-container { max-width: 1200px; margin: 0 auto; padding: 20px; }
        .tender-card {
            background: white; border-radius: 12px; padding: 20px; margin: 15px 0;
            border: 1px solid #e2e8f0; box-shadow: 0 2px 8px rgba(0,0,0,0.1);
            transition: all 0.3s;
        }
        .tender-card:hover {
            transform: translateY(-2px); box-shadow: 0 8px 25px rgba(0,0,0,0.15);
        }
        .tender-title {
            font-size: 1.1rem; font-weight: 600; margin-bottom: 12px; 
            color: #334155; cursor: pointer;
        }
        .tender-meta {
            display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 10px; margin: 10px 0;
        }
        .meta-item {
            display: flex; align-items: center; gap: 8px;
            color: #64748b; font-size: 0.9rem;
        }
        .tender-actions {
            display: flex; gap: 10px; margin-top: 15px;
            padding-top: 15px; border-top: 1px solid #e2e8f0;
        }
        .action-btn {
            background: #f8fafc; color: #334155; padding: 8px 16px;
            border: none; border-radius: 6px; font-size: 0.8rem; cursor: pointer;
            transition: all 0.2s;
        }
        .action-btn:hover { background: #3b82f6; color: white; }
        
        /* Document download notice */
        .download-notice {
            background: #fef3c7; border: 1px solid #f59e0b;
            padding: 10px; border-radius: 6px; margin: 10px 0;
            font-size: 0.85rem; color: #92400e;
        }
    </style>
</head>
<body>
    <div class="tenders-container" id="tenders-container">
        <div style="text-align: center; padding: 40px;">Loading tenders...</div>
    </div>

    <script>
        const API_KEY = 'YOUR_API_KEY_HERE';
        const API_BASE = 'https://api.tendersoko.africa/api/v1';

        // Load tenders on page load
        document.addEventListener('DOMContentLoaded', function() {
            loadTenders();
        });

        async function loadTenders() {
            try {
                const response = await fetch(`${API_BASE}/tenders`, {
                    headers: {
                        'Authorization': 'Bearer ' + API_KEY
                    }
                });
                const data = await response.json();

                if (data.status === 'OK') {
                    displayTenders(data.tenders);
                } else {
                    showError('Failed to load tenders: ' + data.message);
                }
            } catch (error) {
                showError('Network error: ' + error.message);
            }
        }

        function displayTenders(tenders) {
            const container = document.getElementById('tenders-container');
            
            if (tenders.length === 0) {
                container.innerHTML = '<div style="text-align: center; padding: 40px;">No tenders found</div>';
                return;
            }

            container.innerHTML = tenders.map(tender => `
                <div class="tender-card">
                    <div class="tender-title" onclick="viewTenderDetails(${tender.id})">
                        ${escapeHtml(tender.title)}
                    </div>
                    <div class="tender-meta">
                        <div class="meta-item">
                            <i>📍</i>
                            <span>${escapeHtml(tender.county)} • ${escapeHtml(tender.country_name || tender.country)}</span>
                        </div>
                        <div class="meta-item">
                            <i>📅</i>
                            <span>Closes: ${tender.closing_date} (${tender.days_remaining} days left)</span>
                        </div>
                        <div class="meta-item">
                            <i>🛠️</i>
                            <span>${escapeHtml(tender.category_name || 'General')}</span>
                        </div>
                        <div class="meta-item">
                            <i>📄</i>
                            <span>${tender.docsCount || 0} Documents</span>
                        </div>
                    </div>
                    <div class="tender-actions">
                        <button class="action-btn" onclick="viewTenderDetails(${tender.id})">
                            View Details
                        </button>
                        <button class="action-btn" onclick="downloadTenderAdvert(${tender.id})">
                            Download Advert
                        </button>
                    </div>
                </div>
            `).join('');
        }

        function downloadTenderAdvert(tenderId) {
            // Show notice about download
            const notice = document.createElement('div');
            notice.className = 'download-notice';
            notice.innerHTML = '<i class="fas fa-info-circle"></i> Download starting...';
            document.body.appendChild(notice);
            setTimeout(() => notice.remove(), 3000);
            
            // Trigger download using iframe method
            const iframe = document.createElement('iframe');
            iframe.style.display = 'none';
            iframe.src = `${API_BASE}/advert_doc/${tenderId}`;
            document.body.appendChild(iframe);
            
            // Cleanup
            setTimeout(() => {
                document.body.removeChild(iframe);
            }, 5000);
        }

        async function viewTenderDetails(tenderId) {
            try {
                const response = await fetch(`${API_BASE}/tenders/${tenderId}`, {
                    headers: {
                        'Authorization': 'Bearer ' + API_KEY
                    }
                });
                const data = await response.json();
                
                if (data.status === 'OK') {
                    const tender = data.tender;
                    
                    // Display description (already sanitized by API)
                    const description = tender.description || 'No description available';
                    
                    const modal = document.createElement('div');
                    modal.style.cssText = 'position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 1000; display: flex; align-items: center; justify-content: center;';
                    modal.innerHTML = `
                        <div style="background: white; padding: 30px; border-radius: 12px; max-width: 800px; width: 90%; max-height: 80vh; overflow-y: auto;">
                            <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
                                <h3 style="margin: 0;">${escapeHtml(tender.title)}</h3>
                                <button onclick="this.closest('[style*=\"position: fixed\"]').remove()" style="background: none; border: none; font-size: 1.5rem; cursor: pointer; color: #64748b;">&times;</button>
                            </div>
                            <div>
                                <p><strong>Company:</strong> ${escapeHtml(tender.company)}</p>
                                <p><strong>Location:</strong> ${escapeHtml(tender.county)}, ${escapeHtml(tender.country_name)}</p>
                                <p><strong>Closing Date:</strong> ${tender.closing_date}</p>
                                <p><strong>Description:</strong></p>
                                <div style="background: #f8fafc; padding: 15px; border-radius: 6px; line-height: 1.6;">
                                    ${description}
                                </div>
                                <div style="display: flex; gap: 10px; margin-top: 20px;">
                                    <button class="action-btn" onclick="downloadTenderAdvert(${tender.id})">
                                        Download Advert
                                    </button>
                                </div>
                            </div>
                        </div>
                    `;
                    document.body.appendChild(modal);
                }
            } catch (error) {
                alert('Failed to load tender details');
            }
        }

        function showError(message) {
            const container = document.getElementById('tenders-container');
            container.innerHTML = \`<div style="color: #ef4444; text-align: center; padding: 20px;">\${escapeHtml(message)}</div>\`;
        }

        // HTML escaping function
        function escapeHtml(text) {
            const div = document.createElement('div');
            div.textContent = text;
            return div.innerHTML;
        }
    </script>
</body>
</html>

🎯 Advanced - Filtered Tenders with Country Selection

Enhanced integration with filtering capabilities:

Filtered Tenders Integration (Secure)
// Advanced integration with filtering
const API_KEY = 'YOUR_API_KEY_HERE';
const API_BASE = 'https://api.tendersoko.africa/api/v1';

let countries = [];
let categories = [];
let sectors = [];

// Initialize on page load
document.addEventListener('DOMContentLoaded', async function() {
    await loadReferenceData();
    await loadTenders();
});

// Load reference data for filters
async function loadReferenceData() {
    try {
        // Load countries with secure headers
        const countriesResponse = await fetch(`${API_BASE}/countries`, {
            headers: {
                'Authorization': 'Bearer ' + API_KEY
            }
        });
        const countriesData = await countriesResponse.json();
        if (countriesData.status === true) {
            countries = countriesData.countries;
            populateCountryFilter();
        }

        // Load categories with secure headers
        const categoriesResponse = await fetch(`${API_BASE}/categories`, {
            headers: {
                'Authorization': 'Bearer ' + API_KEY
            }
        });
        const categoriesData = await categoriesResponse.json();
        if (categoriesData.status === true) {
            categories = Object.entries(categoriesData.categories).map(([id, name]) => ({ id: parseInt(id), name }));
            populateCategoryFilter();
        }

        // Load sectors with secure headers
        const sectorsResponse = await fetch(`${API_BASE}/sectors`, {
            headers: {
                'Authorization': 'Bearer ' + API_KEY
            }
        });
        const sectorsData = await sectorsResponse.json();
        if (sectorsData.status === true) {
            sectors = sectorsData.sectors;
            populateSectorFilter();
        }
    } catch (error) {
        console.error('Failed to load reference data:', error);
    }
}

// Load tenders with filters using secure headers
async function loadTenders(filters = {}) {
    try {
        const params = new URLSearchParams();
        
        // Add filters but NOT the API key
        if (filters.country) params.append('country', filters.country);
        if (filters.category) params.append('category', filters.category);
        if (filters.sector) params.append('sector', filters.sector);
        if (filters.county) params.append('county', filters.county);
        
        if (filters.type === 'today') {
            params.append('action', 'today');
        } else if (filters.type === 'closing') {
            params.append('action', 'closing');
            if (filters.days) params.append('days', filters.days);
        }

        const endpoint = filters.type === 'today' ? '/today' : 
                        filters.type === 'closing' ? '/closing' : '/tenders';
        
        const response = await fetch(`${API_BASE}${endpoint}?${params.toString()}`, {
            headers: {
                'Authorization': 'Bearer ' + API_KEY
            }
        });
        const data = await response.json();

        if (data.status === 'OK') {
            displayTenders(data.tenders);
        } else {
            showError('Failed to load tenders: ' + data.message);
        }
    } catch (error) {
        showError('Network error: ' + error.message);
    }
}

// Display tenders with proper escaping
function displayTenders(tenders) {
    const container = document.getElementById('tenders-container');
    
    if (tenders.length === 0) {
        container.innerHTML = '<div style="text-align: center; padding: 40px;">No tenders found</div>';
        return;
    }

    container.innerHTML = tenders.map(tender => {
        const safeTitle = escapeHtml(tender.title);
        const safeCompany = escapeHtml(tender.company || 'N/A');
        const safeCounty = escapeHtml(tender.county || 'N/A');
        const safeCountry = escapeHtml(tender.country_name || tender.country || 'N/A');
        const safeCategory = escapeHtml(tender.category_name || 'General');
        
        return `
            <div class="tender-card">
                <div class="tender-title" onclick="viewTenderDetails(${tender.id})">
                    ${safeTitle}
                </div>
                <div class="tender-meta">
                    <div class="meta-item">
                        <i>📍</i>
                        <span>${safeCounty} • ${safeCountry}</span>
                    </div>
                    <div class="meta-item">
                        <i>📅</i>
                        <span>Closes: ${tender.closing_date} (${tender.days_remaining} days left)</span>
                    </div>
                    <div class="meta-item">
                        <i>🏢</i>
                        <span>${safeCompany}</span>
                    </div>
                    <div class="meta-item">
                        <i>🛠️</i>
                        <span>${safeCategory}</span>
                    </div>
                </div>
                <div class="tender-actions">
                    <button class="action-btn" onclick="viewTenderDetails(${tender.id})">
                        View Details
                    </button>
                    <button class="action-btn" onclick="downloadTenderAdvert(${tender.id})">
                        Download Advert
                    </button>
                </div>
            </div>
        `;
    }).join('');
}

// View tender details
async function viewTenderDetails(tenderId) {
    try {
        const response = await fetch(`${API_BASE}/tenders/${tenderId}`, {
            headers: {
                'Authorization': 'Bearer ' + API_KEY
            }
        });
        const data = await response.json();
        
        if (data.status === 'OK') {
            const tender = data.tender;
            
            const safeTitle = escapeHtml(tender.title);
            const safeCompany = escapeHtml(tender.company);
            const safeCounty = escapeHtml(tender.county);
            const safeCountry = escapeHtml(tender.country_name);
            const description = tender.description || 'No description available';
            
            const modal = document.createElement('div');
            modal.style.cssText = 'position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 1000; display: flex; align-items: center; justify-content: center;';
            modal.innerHTML = `
                <div style="background: white; padding: 30px; border-radius: 12px; max-width: 800px; width: 90%; max-height: 80vh; overflow-y: auto;">
                    <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
                        <h3 style="margin: 0;">${safeTitle}</h3>
                        <button onclick="this.closest('[style*=\"position: fixed\"]').remove()" style="background: none; border: none; font-size: 1.5rem; cursor: pointer; color: #64748b;">&times;</button>
                    </div>
                    <div>
                        <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-bottom: 20px;">
                            <div>
                                <strong>Company:</strong>
                                <p>${safeCompany}</p>
                            </div>
                            <div>
                                <strong>Location:</strong>
                                <p>${safeCounty}, ${safeCountry}</p>
                            </div>
                            <div>
                                <strong>Closing Date:</strong>
                                <p>${tender.closing_date}</p>
                            </div>
                            <div>
                                <strong>Sector:</strong>
                                <p>${escapeHtml(tender.sector || 'Not specified')}</p>
                            </div>
                        </div>
                        
                        <p><strong>Description:</strong></p>
                        <div style="background: #f8fafc; padding: 15px; border-radius: 6px; line-height: 1.6;">
                            ${description}
                        </div>
                        
                        <div style="display: flex; gap: 10px; margin-top: 20px;">
                            <button class="action-btn" onclick="downloadTenderAdvert(${tender.id})">
                                Download Advert
                            </button>
                        </div>
                    </div>
                </div>
            `;
            document.body.appendChild(modal);
        }
    } catch (error) {
        alert('Failed to load tender details');
    }
}

// Download tender advert
function downloadTenderAdvert(tenderId) {
    const iframe = document.createElement('iframe');
    iframe.style.display = 'none';
    iframe.src = \`\${API_BASE}/advert_doc/\${tenderId}\`;
    document.body.appendChild(iframe);
    
    setTimeout(() => {
        if (iframe.parentNode) {
            document.body.removeChild(iframe);
        }
    }, 5000);
}

// Filter functions
function filterByCountry(countryCode) {
    loadTenders({ country: countryCode });
}

function filterByCategory(categoryId) {
    loadTenders({ category: categoryId });
}

function showTodaysTenders() {
    loadTenders({ type: 'today' });
}

function showClosingTenders(days = 7) {
    loadTenders({ type: 'closing', days: days });
}

// HTML escaping function
function escapeHtml(text) {
    const div = document.createElement('div');
    div.textContent = text;
    return div.innerHTML;
}

// Helper functions to populate filter dropdowns
function populateCountryFilter() {
    const select = document.getElementById('country-filter');
    if (select && countries.length > 0) {
        select.innerHTML = '<option value="">All Countries</option>' +
            countries.map(country => 
                \`<option value="\${country.code}">\${country.name}</option>\`
            ).join('');
    }
}

function populateCategoryFilter() {
    const select = document.getElementById('category-filter');
    if (select && categories.length > 0) {
        select.innerHTML = '<option value="">All Categories</option>' +
            categories.map(category => 
                \`<option value="\${category.id}">\${category.name}</option>\`
            ).join('');
    }
}

function populateSectorFilter() {
    const select = document.getElementById('sector-filter');
    if (select && sectors.length > 0) {
        select.innerHTML = '<option value="">All Sectors</option>' +
            sectors.map(sector => 
                \`<option value="\${sector.id}">\${sector.name}</option>\`
            ).join('');
    }
}

// Example usage:
// filterByCountry('KE'); // Get tenders for Kenya
// filterByCountry('TZ'); // Get tenders for Tanzania
// filterByCategory(5);   // Get tenders in category ID 5

📁 Professional Document Management

Complete solution for handling document downloads:

Document Manager Class
// Professional Document Manager for Tendersoko API
class TendersokoDocumentManager {
    constructor(apiKey) {
        this.apiKey = apiKey;
        this.apiBase = 'https://api.tendersoko.africa/api/v1';
    }
    
    /**
     * Download tender advert - uses iframe method
     */
    downloadAdvert(tenderId) {
        const iframe = document.createElement('iframe');
        iframe.style.display = 'none';
        iframe.src = `${this.apiBase}/advert_doc/${tenderId}`;
        document.body.appendChild(iframe);
        
        setTimeout(() => {
            if (iframe.parentNode) {
                document.body.removeChild(iframe);
            }
        }, 5000);
        
        return { success: true };
    }
    
    /**
     * Download document
     */
    downloadDocument(docId) {
        const iframe = document.createElement('iframe');
        iframe.style.display = 'none';
        iframe.src = `${this.apiBase}/documents/${docId}/download`;
        document.body.appendChild(iframe);
        
        setTimeout(() => {
            if (iframe.parentNode) {
                document.body.removeChild(iframe);
            }
        }, 5000);
        
        return { success: true };
    }
    
    /**
     * Get document list for a tender
     */
    async getTenderDocuments(tenderId) {
        try {
            const url = `${this.apiBase}/tenders/${tenderId}/documents`;
            const response = await fetch(url, {
                headers: {
                    'Authorization': 'Bearer ' + this.apiKey
                }
            });
            
            if (!response.ok) {
                throw new Error(`HTTP ${response.status}`);
            }
            
            const data = await response.json();
            
            if (data.status && data.documents) {
                return {
                    success: true,
                    documents: data.documents
                };
            } else {
                return { success: false, error: 'No documents found', documents: [] };
            }
            
        } catch (error) {
            return { success: false, error: error.message, documents: [] };
        }
    }
}

// Usage Example
const documentManager = new TendersokoDocumentManager('YOUR_API_KEY_HERE');

// Download advert
documentManager.downloadAdvert(1);

// Download specific document
documentManager.downloadDocument(1);

// Get and display documents
documentManager.getTenderDocuments(1)
    .then(result => {
        if (result.success) {
            console.log(`Found ${result.documents.length} documents`);
        }
    });

🔒 Security Best Practices Guide

Critical Security Notes:
  • Never expose API keys in client-side JavaScript for production applications
  • Domain locking restricts API usage to registered websites only
  • Use server-side proxies for production deployments
  • Regularly rotate API keys from your dashboard

Recommended Production Architecture:

Server-Side Proxy Pattern
// Node.js/Express example for secure server-side proxy
const express = require('express');
const axios = require('axios');
const app = express();

// Your secure endpoint that hides the API key
app.get('/api/tenders', async (req, res) => {
    try {
        const response = await axios.get('https://api.tendersoko.africa/api/v1/tenders', {
            headers: {
                'Authorization': 'Bearer YOUR_SERVER_API_KEY'
            },
            params: req.query
        });
        
        res.json(response.data);
    } catch (error) {
        res.status(error.response?.status || 500).json({
            error: 'Failed to fetch tenders'
        });
    }
});

// Document download proxy
app.get('/api/documents/:id/download', async (req, res) => {
    try {
        const response = await axios.get(`https://api.tendersoko.africa/api/v1/documents/${req.params.id}/download`, {
            headers: {
                'Authorization': 'Bearer YOUR_SERVER_API_KEY'
            },
            responseType: 'stream'
        });
        
        res.set('Content-Type', response.headers['content-type']);
        res.set('Content-Disposition', response.headers['content-disposition']);
        
        response.data.pipe(res);
    } catch (error) {
        res.status(error.response?.status || 500).json({
            error: 'Failed to download document'
        });
    }
});

// Advert download proxy
app.get('/api/advert_doc/:id', async (req, res) => {
    try {
        const response = await axios.get(`https://api.tendersoko.africa/api/v1/advert_doc/${req.params.id}`, {
            headers: {
                'Authorization': 'Bearer YOUR_SERVER_API_KEY'
            },
            responseType: 'stream'
        });
        
        res.set('Content-Type', response.headers['content-type']);
        res.set('Content-Disposition', response.headers['content-disposition']);
        
        response.data.pipe(res);
    } catch (error) {
        res.status(error.response?.status || 500).json({
            error: 'Failed to download advert'
        });
    }
});