A cross-module workflow: create a deal, attach a VDR room, register a webhook, upload a document, and receive the notification.
This quickstart uses three modules: CRM (the deal), VDR (the data room), and Webhooks (the notification). By the end you'll have a working pipeline that fires a webhook every time someone uploads a document to your deal room — the building block of any diligence automation.
Get an API key
Every request needs a Bearer token. Register once, reuse the key forever.
Visit kela.com/developers/register and create an account. Your key (starts with vdr_) appears on the dashboard — copy it now, it shows only once.
export FUNDOS_API_KEY=vdr_your_key_here
Add it to your shell profile (~/.zshenv for non-interactive shells) so it persists.
Create a deal in the CRM
The deal is the anchor. Every VDR room, transaction, and pricing model traces back to it.
curl -X POST "https://kela.com/api/v1/fundos/crm/deals?ephemeral=true" \
-H "Authorization: Bearer $FUNDOS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Acme Series B", "stage": "sourcing", "asset_class": "equity"}'
# → {"success": true, "data": {"id": null, "name": "Acme Series B", "stage": "sourcing"}}
curl -X POST https://kela.com/api/v1/fundos/crm/deals \
-H "Authorization: Bearer $FUNDOS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Acme Series B", "stage": "sourcing", "asset_class": "equity"}'
# → {"success": true, "data": {"id": 42, "name": "Acme Series B", "stage": "sourcing"}}
DEAL_ID=42
?ephemeral=true validates your request body and returns the shape without writing anything. Always dry-run first.
Create a VDR room and link it to the deal
VDR rooms are how counterparties share documents. A deal gets its room via a Transaction — not a direct field.
curl -X POST https://kela.com/api/v1/rooms \
-H "Authorization: Bearer $FUNDOS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Acme DD Room", "room_type": "company_investment"}'
# → {"success": true, "data": {"id": 12, "name": "Acme DD Room"}}
ROOM_ID=12
curl -X POST https://kela.com/api/v1/fundos/transactions \
-H "Authorization: Bearer $FUNDOS_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"name\": \"Acme Series B Closing\", \"deal_id\": $DEAL_ID}"
# Note the transaction id, then link the room:
TX_ID=9
curl -X PATCH "https://kela.com/api/v1/fundos/transactions/$TX_ID" \
-H "Authorization: Bearer $FUNDOS_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"linked_room_id\": $ROOM_ID}"
room_id. The canonical join is Transaction.linked_room_id. Use GET /api/v1/fundos/transactions?deal_id={id} to find the room from a deal later.
Register a webhook for document.uploaded
Instead of polling every 5 minutes, let FundOS push to you the instant someone uploads a document.
For testing, use webhook.site — it gives you a free HTTPS endpoint that logs incoming requests. Copy your unique URL.
HOOK_URL="https://webhook.site/your-unique-id-here"
curl -X POST https://kela.com/api/v1/webhooks/ \
-H "Authorization: Bearer $FUNDOS_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"url\": \"$HOOK_URL\",
\"name\": \"Document alerts\",
\"event_types\": [\"document.uploaded\"]
}"
# → {"id": 1, "secret": "abc123...", "event_types": ["document.uploaded"]}
# Save the secret — verify signatures in production with HMAC-SHA256
sha256=hmac(secret, raw_body) matches X-FundOS-Signature.
Upload a document to the room
This is the action that triggers the webhook. In production, counterparties upload via the FundOS UI — your app gets notified automatically.
# Create a test file
echo "Pitch deck placeholder" > pitch_deck.pdf
# Upload it
curl -X POST "https://kela.com/api/v1/rooms/$ROOM_ID/documents" \
-H "Authorization: Bearer $FUNDOS_API_KEY" \
-F "file=@pitch_deck.pdf"
# → {"success": true, "data": {"id": 88, "original_filename": "pitch_deck.pdf"}}
Receive the webhook notification
Switch to webhook.site — you'll see the payload arrive within ~2 seconds.
Open webhook.site in your browser. After uploading in Step 5, you should see a new request with this shape:
{
"event": "document.uploaded",
"version": "1.0",
"delivery_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-06-12T10:00:00.000Z",
"data": {
"room_id": 12,
"document_id": 88,
"filename": "pitch_deck.pdf",
"uploader_id": 1
}
}
Use delivery_id to deduplicate retries. FundOS retries up to 6 times with exponential backoff if your endpoint doesn't respond 200.
Now that you have the foundation, extend it:
POST /api/v1/rooms/{id}/search with a query string — returns cited answers from uploaded documentsdeal.stage_changed to trigger downstream tasks when a deal advances to "closing"capital_call.issued to trigger LP email reminders via your own email servicecovenant.breached to page your risk team instantly