Quickstart — 60 seconds to your first memo
Four steps. You'll have an institutional-grade investment memo at analysis.proplace.co/<slug> in roughly 8 minutes.
-
1Get an API token
Email alexandre@proplace.co. We provision a personal token + onboard your
fund_typein Airtable. -
2List the funds you can target
Confirm your
fund_typeis live:curl -H "Authorization: Bearer $PROPLACE_TOKEN" \ https://alexandre-79537--sourcing-agent-fastapi-app.modal.run/v1/funds -
3Trigger a memo
curl -X POST https://alexandre-79537--sourcing-agent-fastapi-app.modal.run/v1/memos \ -H "Authorization: Bearer $PROPLACE_TOKEN" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ "fund_type": "s4bt", "company": { "name": "Thrust Carbon", "website": "https://thrustcarbon.com" }, "memo_type": "detailed" }'import os, uuid, httpx resp = httpx.post( "https://alexandre-79537--sourcing-agent-fastapi-app.modal.run/v1/memos", headers={ "Authorization": f"Bearer {os.environ['PROPLACE_TOKEN']}", "Idempotency-Key": str(uuid.uuid4()), }, json={ "fund_type": "s4bt", "company": {"name": "Thrust Carbon", "website": "https://thrustcarbon.com"}, "memo_type": "detailed", }, timeout=30, ) print(resp.json()) # {"slug": "thrust-carbon-v2", "poll_url": "/v1/memos/thrust-carbon-v2", ...}import { randomUUID } from "node:crypto"; const r = await fetch("https://alexandre-79537--sourcing-agent-fastapi-app.modal.run/v1/memos", { method: "POST", headers: { "Authorization": `Bearer ${process.env.PROPLACE_TOKEN}`, "Content-Type": "application/json", "Idempotency-Key": randomUUID(), }, body: JSON.stringify({ fund_type: "s4bt", company: { name: "Thrust Carbon", website: "https://thrustcarbon.com" }, memo_type: "detailed", }), }); console.log(await r.json()); -
4Poll, then read your memo
Poll every 30 seconds — when
statusflips toready, the memo is published aturl.curl -H "Authorization: Bearer $PROPLACE_TOKEN" \ https://alexandre-79537--sourcing-agent-fastapi-app.modal.run/v1/memos/thrust-carbon-v2 # {"status": "ready", "url": "https://analysis.proplace.co/thrust-carbon-v2", "verdict": "CONSIDER", ...}
/v1/screenGet a verdict + thesis-fit score in 10 seconds before committing 8 minutes to a full memo.
Concepts
Four objects you'll encounter. Worth 60 seconds.
fund_type — the compounding key
Every memo is written through the lens of a specific fund. The fund_type identifier resolves (server-side) to the full fund thesis, sector profile, return hurdles, M&A acquirer context, and language preference. You never send the thesis itself — your IP stays at home. Discover available fund_types with GET /v1/funds.
memo — the artifact
A memo is a 30-page institutional investment dossier (market sizing, value chain, competitive positioning, financial model, conviction, risk register, deal rationale). It runs three flavors:
- fast~3 min · executive summary + verdict only
- quick~5 min · core analysis without value-chain deep dive
- detailed~8 min · full pipeline (recommended)
slug — the URL key
The slug is derived deterministically from the company name (NFKD normalize → lowercase → kebab-case). A company called "Thrust Carbon" becomes thrust-carbon-v2, published at analysis.proplace.co/thrust-carbon-v2. The same company name will always produce the same slug — re-triggering overwrites the memo.
verdict — the decision
The synthetic GP returns one of: MUST_CALL, CONSIDER, MONITOR, PASS, HARD_BLOCK. The verdict is consistent across /screen (10s pre-flight) and the full memo's /memos/{slug} response.
Authentication
Every endpoint requires a Bearer token in the Authorization header.
Authorization: Bearer pk_live_3f4a...e9c2
Tokens are scoped to your fund(s) and rate-limited per token. Rotation: email us — we issue a new token, revoke the old one.
Token best practices
- Never commit tokens. Use environment variables or your secret manager.
- One token per integration. Easier to rotate when you offboard a service.
- Use Idempotency-Key on POST requests. Safe to retry without duplicating memos. See error handling.
API reference
Six endpoints. All under https://alexandre-79537--sourcing-agent-fastapi-app.modal.run/v1. Full machine-readable spec: openapi.json.
/v1/funds
List every fund_type the caller can target.
listFunds
Show example ↓
[
{
"fund_type": "s4bt",
"display_name": "Solutions for Business Travel",
"lang": "en",
"supports_synergy_model": true,
"logo_url": "https://..."
}
]
/v1/funds/{fund_type}
Fetch a fund's metadata + a redacted summary of its thesis (sectors, stage, ticket size). Full thesis stays internal.
getFund
/v1/screen
10-second pre-flight verdict. Returns verdict, score, estimated ARR. Use before committing to a full memo.
screenDeal
Show request/response ↓
{
"fund_type": "s4bt",
"company": {
"name": "Thrust Carbon",
"website": "https://thrustcarbon.com"
}
}
{
"fund_type": "s4bt",
"company_name": "Thrust Carbon",
"verdict": "CONSIDER",
"score": 75.0,
"arr_mid_eur": 1000000.0,
"next_step": "Call POST /v1/memos for full memo"
}
/v1/memos
202 async
Trigger the full memo pipeline. Returns immediately with a slug + poll_url.
triggerMemo
Show request/response ↓
{
"fund_type": "s4bt",
"company": { "name": "Thrust Carbon" },
"memo_type": "detailed"
}
{
"memo_id": "thrust-carbon-v2",
"slug": "thrust-carbon-v2",
"url": "https://analysis.proplace.co/thrust-carbon-v2",
"status": "queued",
"fund_type": "s4bt",
"memo_type": "detailed",
"estimated_seconds": 540,
"poll_url": "/v1/memos/thrust-carbon-v2"
}
{
"fund_type": "s4bt",
"company": {
"name": "Thrust Carbon",
"website": "https://thrustcarbon.com",
"linkedin": "https://www.linkedin.com/in/kit-aspen",
"tagline": "Carbon accounting for travel managers",
"country": "United Kingdom",
"company_stage": "seed",
"founders_cofounders": "Kit Aspen (CEO, ex-GoCardless), ...",
"funding": "€4M",
"funding_date": "2024-03-15",
"vc_funds": "Seedcamp, Octopus Ventures",
"pitch_deck_url": "https://drive.google.com/...",
"product_url": "https://thrustcarbon.com/product",
"pricing_url": "https://thrustcarbon.com/pricing",
"team_url": "https://thrustcarbon.com/team",
"first_name": "Kit",
"last_name": "Aspen"
},
"memo_type": "detailed",
"lang": "en",
"synergies_with": "rec123abc",
"publish": "yes",
"motive": "Strategic bolt-on for CSRD compliance moat",
"source": "linkedin",
"callback_url": "https://yourapp.example/proplace-callback"
}
| Field | Type | Required | Notes |
|---|---|---|---|
fund_type | string | Yes | From GET /v1/funds |
company.name | string | Yes | Drives the slug |
company.website | string | — | Recommended for accurate research |
company.linkedin | string | — | CEO/founder personal LinkedIn URL — format https://www.linkedin.com/in/firstname-lastname (NOT the company page) |
company.tagline | string | — | One-line value prop |
company.country | string | — | HQ country, full name |
company.company_stage | string | — | seed / series_a / series_b… |
company.founders_cofounders | string | — | Free text on the founding team |
company.funding | string | — | Latest round (e.g. €4M) |
company.funding_date | string | — | Date or free text |
company.vc_funds | string | — | Existing investors (comma-separated) |
company.pitch_deck_url | string | — | Public PDF URL |
company.product_url / pricing_url / team_url | string | — | Override deep-research targets |
company.first_name / last_name | string | — | CEO name hint |
memo_type | enum | — | fast · quick · detailed (default) |
lang | string | — | Defaults to fund.lang |
synergies_with | string | — | Airtable record_id of a portfolio company |
publish | string | — | Set to "yes" for the Open Deal Flow |
motive | string | — | Strategic motive (free text) |
source | string | — | Where the deal came from (twitter, linkedin, inbound…) |
callback_url | string | — | Experimental — POST'd when the memo is ready |
⚡ Pass Idempotency-Key header (any UUID) to safely retry without duplicating work.
/v1/memos/{slug}
Poll memo status. Returns queued → building → ready (or failed).
getMemo
Show example ↓
{
"slug": "thrust-carbon-v2",
"url": "https://analysis.proplace.co/thrust-carbon-v2",
"status": "ready",
"fund_type": "s4bt",
"company_name": "Thrust Carbon",
"memo_type": "detailed",
"verdict": "CONSIDER",
"summary": "Strong CSRD compliance moat..."
}
/v1/memos/{slug}/patch
Surgically edit a published memo. 5 operation types: replace_section, replace_section_inner, append_to_section, prepend_to_section, replace_text.
patchMemo
Show example ↓
{
"operations": [
{
"type": "replace_section_inner",
"section_id": "conviction",
"html": "Conviction
New analysis...
"
},
{
"type": "replace_text",
"find": "ARR €1M",
"replace": "ARR €1.5M"
}
],
"dry_run": false,
"commit_message": "Update conviction post-call"
}
Set dry_run: true to preview the diff without pushing.
Integration recipes
Ten ways to plug Proplace into your stack. Pick one, ship in 5 minutes.
curl
The lowest common denominator. Runs from any shell, CI job, or server.
export PROPLACE_TOKEN=pk_live_...
curl -X POST https://alexandre-79537--sourcing-agent-fastapi-app.modal.run/v1/memos \
-H "Authorization: Bearer $PROPLACE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"fund_type":"s4bt","company":{"name":"Acme"},"memo_type":"detailed"}'
Python (httpx)
import os, time, httpx
API = "https://alexandre-79537--sourcing-agent-fastapi-app.modal.run/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['PROPLACE_TOKEN']}"}
def trigger_and_wait(fund_type: str, company_name: str) -> dict:
r = httpx.post(f"{API}/memos", headers=HEADERS, json={
"fund_type": fund_type,
"company": {"name": company_name},
"memo_type": "detailed",
}).raise_for_status().json()
slug = r["slug"]
while True:
time.sleep(30)
s = httpx.get(f"{API}/memos/{slug}", headers=HEADERS).json()
if s["status"] in ("ready", "failed"): return s
print(trigger_and_wait("s4bt", "Thrust Carbon"))
Node.js (fetch)
const API = "https://alexandre-79537--sourcing-agent-fastapi-app.modal.run/v1";
const TOKEN = process.env.PROPLACE_TOKEN;
async function triggerAndWait(fundType, companyName) {
const r = await fetch(`${API}/memos`, {
method: "POST",
headers: { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json" },
body: JSON.stringify({ fund_type: fundType, company: { name: companyName }, memo_type: "detailed" }),
}).then(r => r.json());
while (true) {
await new Promise(r => setTimeout(r, 30_000));
const s = await fetch(`${API}/memos/${r.slug}`, { headers: { Authorization: `Bearer ${TOKEN}` } }).then(r => r.json());
if (["ready", "failed"].includes(s.status)) return s;
}
}
Make.com
HTTP module configuration — paste these fields into a Make HTTP module:
URL: https://alexandre-79537--sourcing-agent-fastapi-app.modal.run/v1/memos
Method: POST
Headers:
Authorization: Bearer YOUR_TOKEN
Content-Type: application/json
Body type: Raw
Content-type: JSON (application/json)
Request body: {"fund_type":"{{1.fund_type}}","company":{"name":"{{1.company_name}}"},"memo_type":"detailed"}
Parse response: Yes
Then chain a Sleep (30s) + HTTP GET on /v1/memos/{{2.slug}} inside a Repeater until status === "ready".
n8n
Use the HTTP Request node with these settings:
Method: POST
URL: https://alexandre-79537--sourcing-agent-fastapi-app.modal.run/v1/memos
Authentication: Header Auth
Name: Authorization
Value: Bearer YOUR_TOKEN
Body Content Type: JSON
Body (JSON):
{
"fund_type": "{{ $json.fund_type }}",
"company": { "name": "{{ $json.company_name }}" },
"memo_type": "detailed"
}
For the polling loop, chain a Wait node (30s) + an IF node checking status === "ready", looping back to a second HTTP Request node on /v1/memos/{{slug}}.
Zapier
Use the Webhooks by Zapier action with these fields:
Action: POST (Webhooks by Zapier)
URL: https://alexandre-79537--sourcing-agent-fastapi-app.modal.run/v1/memos
Payload Type: JSON
Data:
fund_type: {{fund_type}}
company: {"name": "{{company_name}}"}
memo_type: detailed
Headers:
Authorization: Bearer YOUR_TOKEN
Content-Type: application/json
Unflatten: Yes
The 202 response includes a slug you can pass to a follow-up Custom Request → GET step on /v1/memos/<slug>.
ChatGPT (Custom GPT Actions)
In your Custom GPT editor, scroll to Actions → Create new action, then:
Schema: Import from URL
URL: https://alexandre-79537--sourcing-agent-fastapi-app.modal.run/v1/openapi.json
Authentication: API Key
Auth Type: Bearer
API Key: YOUR_TOKEN
Privacy Policy: https://proplace.co/privacy
The 6 actions appear in the GPT: listFunds, getFund, screenDeal, triggerMemo, getMemo, patchMemo.
Sample prompt to test: "List the funds I can target, then run a screen on Thrust Carbon for the first one."
Claude Desktop / Cursor / Windsurf (MCP)
Add this to your MCP config (e.g. ~/.claude/mcp.json for Claude Desktop, or your Cursor/Windsurf settings):
{
"mcpServers": {
"proplace": {
"url": "https://alexandre-79537--sourcing-agent-fastapi-app.modal.run/mcp/mcp",
"headers": { "Authorization": "Bearer YOUR_TOKEN" }
}
}
}
Restart your client. The 6 tools (list_funds, get_fund, screen_deal, trigger_memo, get_memo_status, patch_memo) are now callable in natural language: "Trigger a detailed memo for Thrust Carbon under fund_type s4bt."
Transport: streamable HTTP (JSON-RPC over POST). Protocol version: MCP 2024-11-05.
Errors & rate limits
HTTP status codes
| Code | Meaning | What to do |
|---|---|---|
| 200/202 | Success | — |
| 400 | Invalid fund_type or missing field | Check the available_funds field in the response body |
| 401 / 403 | Token missing or invalid | Verify your Authorization header |
| 404 | Slug or fund_type not found | Spelling — slug is case-sensitive, fund_type is not |
| 409 | Idempotency-key replay | You're seeing the cached original response — safe |
| 429 | Rate limit hit | Honor the Retry-After header |
| 502 | Upstream (Make / GitHub / Airtable) failed | Retry. If persistent, email us. |
Rate limits
Default: 60 requests/minute and 1,000/day per token. Custom quotas available on request. Every response includes:
X-RateLimit-Remaining-MinuteX-RateLimit-Remaining-DailyX-RateLimit-Reset-Minute(unix timestamp)X-RateLimit-Reset-Daily(unix timestamp)
Idempotency
Pass any unique key in the Idempotency-Key header on POST requests. We cache the response for 24 hours — retries with the same key return the original response without re-executing the operation.
Changelog
Six endpoints (listFunds, getFund, screenDeal, triggerMemo, getMemo, patchMemo). MCP server live. ChatGPT Custom GPT compatible.
Ready to plug Proplace into your stack?
Email alexandre@proplace.co with your fund name + use case. Most clients are live in 10 minutes — token + onboarded fund_type + one example memo.
Get your token →