API Reference
The ProposalAI REST API lets you generate AI proposals, manage team members, track engagement, and connect third-party tools — all programmatically.
Authentication
All API requests must be authenticated using a Bearer token. Generate API keys from the Integration Hub → API Keys tab.
Authorization Header
Authorization: Bearer pk_live_a1b2c3d4e5f6...Keep your key secret
| Key format | Environment | Usage |
|---|---|---|
| pk_live_... | Production | Real proposals, real emails, live data |
Proposals
Create, retrieve, and manage enterprise proposals.
/v1/proposals/generateRequires authGenerate a proposal
Uses GPT-4o to generate a fully formatted, region-specific enterprise proposal in HTML. Automatically pulls your company branding from settings.
Request Body
userIdstringrequiredAuthenticated user ID
titlestringrequiredProposal title
clientstringrequiredClient company name
regionstringrequiredTarget region (e.g. UAE, USA, UK, India). Controls laws, VAT, currency, culture context.
industrystringrequiredClient industry vertical
solutionstringrequiredYour solution / product name
scopestringoptionalProject scope and deliverables
commercialobjectoptionalPricing object: { setup, monthly, qty, commitment }
securitystring[]optionalSecurity features list
apisstring[]optionalAPI / integration list
compliancestring[]optionalCompliance certifications
competitorsstringoptionalKnown competitors in the deal
tonestringoptionalProposal tone: formal | consultative | bold
Returns
Proposal object with id, content (HTML), title, status, and branding fields.
Error Codes
curl -X POST https://api.proposalai.ai/v1/proposals/generate \
-H "Authorization: Bearer pk_live_••••" \
-H "Content-Type: application/json" \
-d '{
"userId": "user_abc123",
"title": "AI-Powered Video Analytics for Dubai Metro",
"client": "RTA Dubai",
"region": "UAE",
"industry": "Smart Mobility",
"solution": "ProposalAI AI Analytics Suite",
"commercial": {
"setup": 250000,
"monthly": 18000,
"qty": 1,
"commitment": 36
}
}'/v1/proposals/rfpRequires authAnalyze RFP & generate proposal
Upload a PDF, DOCX, or TXT RFP document. ProposalAI extracts requirements, compliance needs, and evaluation criteria, then generates a matching proposal.
Request Body
fileFilerequiredRFP document (PDF, DOCX, or TXT). Send as multipart/form-data.
userIdstringoptionalIf provided, saves the proposal to the user's account.
Returns
RFP structured extraction + generated proposal content.
Error Codes
curl -X POST https://api.proposalai.ai/v1/proposals/rfp \ -H "Authorization: Bearer pk_live_••••" \ -F "file=@RFP_Document.pdf" \ -F "userId=user_abc123"
/v1/proposals/sendRequires authSend proposal via email
Send a proposal to a client email with open tracking pixel and click tracking link embedded automatically.
Request Body
proposalIdstringrequiredID of the proposal to send
emailstringrequiredRecipient email address
titlestringoptionalEmail subject line (defaults to proposal title)
contentstringoptionalProposal HTML content to embed in the email
Returns
{ success: true } on delivery.
Error Codes
curl -X POST https://api.proposalai.ai/v1/proposals/send \
-H "Authorization: Bearer pk_live_••••" \
-H "Content-Type: application/json" \
-d '{
"proposalId": "prop_9f3a2c1e",
"email": "cto@rtadubai.ae",
"title": "Your AI Analytics Proposal from Acme Corp"
}'Tracking & Analytics
Real-time engagement tracking for sent proposals. These endpoints update the deal score, views, clicks, and open status on proposals.
/v1/tracking/viewRecord a proposal view
Increments the view counter and updates last_viewed timestamp. Updates deal score automatically.
Request Body
idstringrequiredProposal ID
Returns
{ success: true, views: number }
curl -X POST https://api.proposalai.ai/v1/tracking/view \
-H "Content-Type: application/json" \
-d '{ "id": "prop_9f3a2c1e" }'/v1/tracking/openEmail open tracking pixel
Returns a 1×1 transparent GIF. Embed in proposal emails to detect when the recipient opens the email. Sets opened=true on first load.
Query Parameters
idstringrequiredProposal ID
Returns
1×1 GIF image (image/gif). Side effect: marks proposal as opened.
<!-- Embed in email HTML -->
<img src="https://api.proposalai.ai/v1/tracking/open?id=prop_9f3a2c1e"
width="1" height="1" />/v1/tracking/clickEmail click tracking redirect
Increments the click counter then redirects to the proposal share page. Use as the href in proposal email CTAs.
Query Parameters
idstringrequiredProposal ID
Returns
302 redirect to /share/{id}
<!-- Wrap your CTA button --> <a href="https://api.proposalai.ai/v1/tracking/click?id=prop_9f3a2c1e"> View Proposal </a>
Team & Invitations
Manage team members and send branded invitation emails.
/v1/team/inviteRequires authInvite a team member
Creates a team member record with status 'Invited', generates a secure token, and sends a branded HTML invitation email via Resend.
Request Body
ownerIdstringrequiredThe inviting user's ID
namestringrequiredInvitee full name
emailstringrequiredInvitee email address
rolestringrequiredRole: Admin | Manager | Viewer | Editor
permissionsstring[]optionalList of permission strings, e.g. ['proposals:create', 'proposals:send']
Returns
{ success: true, memberId, inviteUrl }
Error Codes
curl -X POST https://api.proposalai.ai/v1/team/invite \
-H "Authorization: Bearer pk_live_••••" \
-H "Content-Type: application/json" \
-d '{
"ownerId": "user_abc123",
"name": "Sara Ahmed",
"email": "sara@yourcompany.com",
"role": "Manager",
"permissions": ["proposals:create", "proposals:send", "proposals:view"]
}'/v1/team/acceptAccept an invitation
Called after the invitee completes signup or login on the invite page. Marks the member as Active and links their auth user ID.
Request Body
memberIdstringrequiredTeam member record ID from the invite lookup
userIdstringrequiredSupabase auth user ID of the invitee
Returns
{ success: true }
curl -X POST https://api.proposalai.ai/v1/team/accept \
-H "Content-Type: application/json" \
-d '{
"memberId": "mem_3f2a1c",
"userId": "auth_user_xyz"
}'Integrations
Manage third-party integration connections for your account.
/v1/integrationsRequires authList integrations
Returns the connection status and masked config for all integrations linked to the user.
Query Parameters
userIdstringrequiredUser ID
Returns
Array of integration objects with name, status, config (masked), and updated_at.
curl https://api.proposalai.ai/v1/integrations?userId=user_abc123 \ -H "Authorization: Bearer pk_live_••••"
/v1/integrations/connectRequires authConnect an integration
Validates credentials against the third-party API, then saves the masked config. Supported: Salesforce, HubSpot, Slack, Stripe, Neon, and more.
Request Body
userIdstringrequiredUser ID
integrationNamestringrequiredIntegration name, e.g. 'Salesforce', 'HubSpot', 'Slack'
configobjectrequiredCredential key-value pairs specific to the integration
Returns
{ success: true }
Error Codes
curl -X POST https://api.proposalai.ai/v1/integrations/connect \
-H "Authorization: Bearer pk_live_••••" \
-H "Content-Type: application/json" \
-d '{
"userId": "user_abc123",
"integrationName": "HubSpot",
"config": {
"access_token": "pat-na1-xxxxxxxx",
"portal_id": "12345678"
}
}'/v1/integrations/disconnectRequires authDisconnect an integration
Removes stored credentials and sets status to disconnected.
Request Body
userIdstringrequiredUser ID
integrationNamestringrequiredIntegration name to disconnect
Returns
{ success: true }
curl -X POST https://api.proposalai.ai/v1/integrations/disconnect \
-H "Authorization: Bearer pk_live_••••" \
-H "Content-Type: application/json" \
-d '{
"userId": "user_abc123",
"integrationName": "HubSpot"
}'API Keys
Programmatically manage API keys for your ProposalAI account.
/v1/api-keysRequires authList API keys
Returns all active API keys for the user. Key values are partially masked.
Query Parameters
userIdstringrequiredUser ID
Returns
Array of API key objects.
curl https://api.proposalai.ai/v1/api-keys?userId=user_abc123 \ -H "Authorization: Bearer pk_live_••••"
/v1/api-keysRequires authCreate API key
Generates a new pk_live_... API key. The full key value is only returned once at creation time.
Request Body
userIdstringrequiredUser ID
namestringrequiredDescriptive name for the key (e.g. Production, CI/CD)
Returns
New key object including the full key_value (only shown once).
curl -X POST https://api.proposalai.ai/v1/api-keys \
-H "Authorization: Bearer pk_live_••••" \
-H "Content-Type: application/json" \
-d '{ "userId": "user_abc123", "name": "Production" }'/v1/api-keys/{id}Requires authRevoke API key
Permanently revokes the specified API key. Any requests using this key will immediately fail with 401.
Query Parameters
userIdstringrequiredUser ID
Returns
{ success: true }
curl -X DELETE "https://api.proposalai.ai/v1/api-keys/key_4d5e6f?userId=user_abc123" \ -H "Authorization: Bearer pk_live_••••"
Webhooks
Register HTTP endpoints to receive real-time event notifications from ProposalAI.
/v1/webhooksRequires authList webhooks
Returns all configured webhook endpoints for the user.
Query Parameters
userIdstringrequiredUser ID
Returns
Array of webhook objects.
curl https://api.proposalai.ai/v1/webhooks?userId=user_abc123 \ -H "Authorization: Bearer pk_live_••••"
/v1/webhooksRequires authCreate webhook
Registers a new webhook endpoint. A whsec_... signing secret is auto-generated and returned. Use it to verify incoming webhook signatures.
Request Body
userIdstringrequiredUser ID
namestringrequiredDisplay name
urlstringrequiredHTTPS endpoint URL
eventsstring[]optionalEvent types to subscribe to. Omit to receive all events.
Returns
Webhook object including the signing secret (shown in full only once).
curl -X POST https://api.proposalai.ai/v1/webhooks \
-H "Authorization: Bearer pk_live_••••" \
-H "Content-Type: application/json" \
-d '{
"userId": "user_abc123",
"name": "CRM Sync",
"url": "https://yourapp.com/webhooks/proposalai",
"events": ["proposal.created", "proposal.accepted", "payment.received"]
}'/v1/webhooks/{id}Requires authPause / resume webhook
Toggle a webhook between active and paused states without deleting it.
Request Body
userIdstringrequiredUser ID
activebooleanrequiredtrue to resume, false to pause
Returns
{ success: true }
curl -X PATCH https://api.proposalai.ai/v1/webhooks/wh_a9b8c7 \
-H "Authorization: Bearer pk_live_••••" \
-H "Content-Type: application/json" \
-d '{ "userId": "user_abc123", "active": false }'/v1/webhooks/{id}Requires authDelete webhook
Permanently removes a webhook endpoint.
Query Parameters
userIdstringrequiredUser ID
Returns
{ success: true }
curl -X DELETE "https://api.proposalai.ai/v1/webhooks/wh_a9b8c7?userId=user_abc123" \ -H "Authorization: Bearer pk_live_••••"
Error Codes
ProposalAI uses standard HTTP status codes. All error responses include a JSON body with an error field.
| Code | Status | Meaning |
|---|---|---|
| 200 | OK | Request succeeded. |
| 400 | Bad Request | Missing or invalid parameters. Check the error message. |
| 401 | Unauthorized | Missing or invalid API key. |
| 403 | Forbidden | Valid key but insufficient permissions. |
| 404 | Not Found | The requested resource does not exist. |
| 410 | Gone | Resource existed but is no longer available (e.g. used invite). |
| 422 | Unprocessable Entity | Validation failed — e.g. invalid credentials when connecting an integration. |
| 429 | Too Many Requests | Rate limit exceeded. Retry after the Retry-After header value. |
| 500 | Internal Server Error | Something went wrong on our end. Contact support. |
Error response format
{
"error": "Credential validation failed. Check your Instance URL and Access Token."
}Webhook Events
Subscribe to specific events when creating a webhook. All payloads are signed with your whsec_... secret using HMAC-SHA256.
| Event | Description |
|---|---|
| proposal.created | A new proposal was generated |
| proposal.sent | A proposal was emailed to a client |
| proposal.viewed | Client opened the proposal share link |
| proposal.accepted | Client accepted the proposal |
| proposal.rejected | Client rejected the proposal |
| proposal.expired | Proposal passed its expiry date |
| team.member_invited | A team invitation was sent |
| team.member_joined | An invitee accepted and joined the team |
| payment.received | A payment was successfully collected |
Verifying webhook signatures
import crypto from "crypto"
function verifyWebhook(payload: string, signature: string, secret: string): boolean {
const expected = crypto
.createHmac("sha256", secret)
.update(payload)
.digest("hex")
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
)
}
// In your endpoint handler:
const sig = req.headers["x-proposalai-signature"]
const body = await req.text()
const valid = verifyWebhook(body, sig, process.env.WEBHOOK_SECRET!)
if (!valid) return res.status(401).send("Invalid signature")Rate Limits
Rate limits are applied per API key. Exceeding limits returns a 429 response with a Retry-After header.
| Endpoint | Limit | Window |
|---|---|---|
| POST /proposals/generate | 20 requests | per minute |
| POST /proposals/rfp | 10 requests | per minute |
| All other endpoints | 100 requests | per minute |
ProposalAI API v1 · Questions? Email support@proposalai.ai