Overview
The ProposalForge API allows you to programmatically create, manage, and generate proposals. Built on Cloudflare Workers with D1 database storage, our API is fast, reliable, and globally distributed.
Base URL: https://your-worker-url.workers.dev
Version: 1.0.0
Protocol: HTTPS only
Format: JSON
Authentication
Currently, the API is open and does not require authentication. Future versions will implement API key authentication for production use.
⚠️ Development Notice: This API is in active development. Breaking changes may occur. Always specify version in production integrations.
Rate Limiting
The API implements rate limiting to ensure fair usage:
- AI Generation: 10 requests per minute per IP
- PDF Generation: 20 requests per minute per IP
- Other Endpoints: 100 requests per minute per IP
Response Format
All API responses follow a consistent JSON structure:
{
"success": true,
"data": { ... },
"message": "Operation successful"
}
Error responses include additional details:
{
"success": false,
"error": "Error message",
"code": "ERROR_CODE",
"details": { ... }
}
Endpoints
POST /api/generate-ai
Generate a proposal using AI based on a project description.
Request Body
| Parameter |
Type |
Required |
Description |
| description |
string |
Required |
Project description (min 20 chars, max 5000 chars) |
| clientName |
string |
Optional |
Client name to include in proposal |
| currency |
string |
Optional |
Preferred currency (default: USD) |
Example Request
POST /api/generate-ai
Content-Type: application/json
{
"description": "Build a responsive e-commerce website with product catalog, shopping cart, and Stripe integration. Timeline: 2 months.",
"clientName": "Acme Corporation",
"currency": "USD"
}
Example Response
{
"success": true,
"data": {
"clientName": "Acme Corporation",
"projectOverview": "Development of a comprehensive e-commerce platform...",
"phases": [
{
"name": "Discovery & Planning",
"description": "Requirements gathering, wireframes, and project planning",
"duration": "1 week",
"price": 3000
},
{
"name": "Frontend Development",
"description": "Responsive UI with product catalog and shopping cart",
"duration": "3 weeks",
"price": 12000
}
],
"currency": "USD",
"totalPrice": 15000,
"paymentTerms": "30% upfront, 40% at midpoint, 30% on completion",
"deliveryTerms": "Weekly progress updates, staged deployment"
},
"message": "Proposal generated successfully"
}
Error Responses
// 400 Bad Request
{
"success": false,
"error": "Description is required and must be at least 20 characters",
"code": "INVALID_DESCRIPTION"
}
// 429 Too Many Requests
{
"success": false,
"error": "Rate limit exceeded. Please try again in 60 seconds",
"code": "RATE_LIMIT_EXCEEDED"
}
// 500 Internal Server Error
{
"success": false,
"error": "AI service temporarily unavailable",
"code": "AI_SERVICE_ERROR"
}
POST /api/generate-pdf
Generate a professional PDF from proposal data.
Request Body
| Parameter |
Type |
Required |
Description |
| clientName |
string |
Required |
Client name |
| clientCompany |
string |
Optional |
Client company name |
| clientEmail |
string |
Optional |
Client email address |
| projectOverview |
string |
Required |
Project description |
| phases |
array |
Required |
Array of phase objects (min 1) |
| currency |
string |
Required |
Currency code (USD, EUR, GBP, etc.) |
| paymentTerms |
string |
Optional |
Payment terms description |
| deliveryTerms |
string |
Optional |
Delivery terms description |
Phase Object Structure
| Property |
Type |
Required |
Description |
| name |
string |
Required |
Phase name |
| description |
string |
Required |
Phase description |
| duration |
string |
Required |
Duration estimate (e.g., "2 weeks") |
| price |
number |
Required |
Phase price (positive number) |
Example Request
POST /api/generate-pdf
Content-Type: application/json
{
"clientName": "John Smith",
"clientCompany": "Acme Corp",
"clientEmail": "john@acme.com",
"projectOverview": "Development of a modern e-commerce platform",
"phases": [
{
"name": "Discovery & Planning",
"description": "Requirements gathering and project planning",
"duration": "1 week",
"price": 3000
},
{
"name": "Development",
"description": "Full stack development of the platform",
"duration": "6 weeks",
"price": 18000
}
],
"currency": "USD",
"paymentTerms": "50% upfront, 50% on completion",
"deliveryTerms": "Weekly progress updates via email"
}
Example Response
{
"success": true,
"data": {
"pdfUrl": "data:application/pdf;base64,JVBERi0xLjcKJeLjz9...",
"fileName": "proposal-20260120-143022.pdf",
"fileSize": 125847,
"totalPrice": 21000
},
"message": "PDF generated successfully"
}
Error Responses
// 400 Bad Request - Missing required fields
{
"success": false,
"error": "Missing required fields: clientName, projectOverview, phases",
"code": "VALIDATION_ERROR"
}
// 400 Bad Request - Invalid phase data
{
"success": false,
"error": "Each phase must have name, description, duration, and price",
"code": "INVALID_PHASE_DATA"
}
// 500 Internal Server Error
{
"success": false,
"error": "PDF generation failed",
"code": "PDF_GENERATION_ERROR"
}
POST /api/proposals
Save a proposal to the database for record keeping.
Request Body
Same structure as /api/generate-pdf endpoint.
Example Response
{
"success": true,
"data": {
"proposalId": "prop_abc123xyz",
"createdAt": "2026-01-20T14:30:22Z"
},
"message": "Proposal saved successfully"
}
GET /api/currencies
Get list of supported currencies with formatting information.
Example Response
{
"success": true,
"data": [
{
"code": "USD",
"name": "US Dollar",
"symbol": "$",
"symbolPosition": "before",
"decimals": 2
},
{
"code": "EUR",
"name": "Euro",
"symbol": "€",
"symbolPosition": "before",
"decimals": 2
},
{
"code": "GBP",
"name": "British Pound",
"symbol": "£",
"symbolPosition": "before",
"decimals": 2
}
],
"message": "Currencies retrieved successfully"
}
SDK & Libraries
JavaScript/TypeScript
Example integration using fetch API:
// Generate AI Proposal
async function generateProposal(description) {
const response = await fetch('https://your-worker.workers.dev/api/generate-ai', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
description,
currency: 'USD'
})
});
const result = await response.json();
if (result.success) {
return result.data;
} else {
throw new Error(result.error);
}
}
// Generate PDF
async function generatePDF(proposalData) {
const response = await fetch('https://your-worker.workers.dev/api/generate-pdf', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(proposalData)
});
const result = await response.json();
if (result.success) {
// Create download link
const link = document.createElement('a');
link.href = result.data.pdfUrl;
link.download = result.data.fileName;
link.click();
} else {
throw new Error(result.error);
}
}
// Usage
const proposal = await generateProposal('Build an e-commerce website...');
await generatePDF(proposal);
Python
Example integration using requests library:
import requests
import json
class ProposalForge:
def __init__(self, base_url):
self.base_url = base_url
def generate_ai_proposal(self, description, currency='USD'):
"""Generate proposal using AI"""
url = f"{self.base_url}/api/generate-ai"
payload = {
"description": description,
"currency": currency
}
response = requests.post(url, json=payload)
result = response.json()
if result['success']:
return result['data']
else:
raise Exception(result['error'])
def generate_pdf(self, proposal_data):
"""Generate PDF from proposal data"""
url = f"{self.base_url}/api/generate-pdf"
response = requests.post(url, json=proposal_data)
result = response.json()
if result['success']:
# Save PDF to file
pdf_data = result['data']['pdfUrl'].split(',')[1]
with open(result['data']['fileName'], 'wb') as f:
f.write(base64.b64decode(pdf_data))
return result['data']['fileName']
else:
raise Exception(result['error'])
# Usage
client = ProposalForge('https://your-worker.workers.dev')
proposal = client.generate_ai_proposal('Build an e-commerce website...')
pdf_file = client.generate_pdf(proposal)
print(f"PDF saved to: {pdf_file}")
PHP
Example integration using cURL:
<?php
class ProposalForge {
private $baseUrl;
public function __construct($baseUrl) {
$this->baseUrl = $baseUrl;
}
public function generateAIProposal($description, $currency = 'USD') {
$url = $this->baseUrl . '/api/generate-ai';
$data = [
'description' => $description,
'currency' => $currency
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode($response, true);
if ($result['success']) {
return $result['data'];
} else {
throw new Exception($result['error']);
}
}
public function generatePDF($proposalData) {
$url = $this->baseUrl . '/api/generate-pdf';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($proposalData));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode($response, true);
if ($result['success']) {
return $result['data'];
} else {
throw new Exception($result['error']);
}
}
}
// Usage
$client = new ProposalForge('https://your-worker.workers.dev');
$proposal = $client->generateAIProposal('Build an e-commerce website...');
$pdf = $client->generatePDF($proposal);
echo "PDF generated: " . $pdf['fileName'];
?>
Webhooks
Webhooks are planned for future releases and will allow you to receive real-time notifications for events such as:
- Proposal created
- PDF generated
- Proposal viewed (future feature)
- Proposal accepted (future feature)
Coming Soon: Webhook support is currently in development. Subscribe to our newsletter to be notified when webhooks become available.
Best Practices
Error Handling
Always implement proper error handling in your integrations:
try {
const proposal = await generateProposal(description);
const pdf = await generatePDF(proposal);
} catch (error) {
if (error.code === 'RATE_LIMIT_EXCEEDED') {
// Wait and retry
await new Promise(resolve => setTimeout(resolve, 60000));
return generateProposal(description);
} else if (error.code === 'AI_SERVICE_ERROR') {
// Fallback to manual creation
showManualForm();
} else {
// Show error to user
showError(error.message);
}
}
Rate Limit Handling
Implement exponential backoff for rate limit errors:
async function retryWithBackoff(fn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (error.code === 'RATE_LIMIT_EXCEEDED' && i < maxRetries - 1) {
const delay = Math.pow(2, i) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
} else {
throw error;
}
}
}
}
Caching
Cache currency list and other static data to reduce API calls:
let currencyCache = null;
let cacheExpiry = null;
async function getCurrencies() {
const now = Date.now();
if (currencyCache && cacheExpiry && now < cacheExpiry) {
return currencyCache;
}
const response = await fetch('/api/currencies');
const result = await response.json();
currencyCache = result.data;
cacheExpiry = now + 3600000; // 1 hour
return currencyCache;
}
Validation
Validate data client-side before sending to the API:
function validateProposal(data) {
const errors = [];
if (!data.clientName || data.clientName.length < 2) {
errors.push('Client name must be at least 2 characters');
}
if (!data.projectOverview || data.projectOverview.length < 20) {
errors.push('Project overview must be at least 20 characters');
}
if (!data.phases || data.phases.length === 0) {
errors.push('At least one phase is required');
}
data.phases?.forEach((phase, index) => {
if (!phase.name) errors.push(`Phase ${index + 1}: Name is required`);
if (!phase.price || phase.price <= 0) {
errors.push(`Phase ${index + 1}: Price must be positive`);
}
});
return errors;
}