← Resources

Fundraising CRM Data Model (custom fields kit)

The complete field set for tracking a raise: what lives on the fund, the partner, and the conversation - and a controlled pass-reason taxonomy that turns this round's rejections into next round's warm list. Ships as a copy-paste JSON kit of enums and custom fields.

Who this is for

Founders about to run a priced round, and the chiefs of staff or AI agents doing the data work behind them. Set the model up before the first outreach email: every field you fill during the raise is either a decision input this round (who to prioritize) or an asset next round (who to re-approach, and when). The stage machinery lives in the companion fundraising pipeline template; this page is the layer underneath it - the data model itself.

The model is tool-agnostic: it works as spreadsheet columns or Notion properties. The JSON artifact at the bottom is for teams who want an agent to run it - every entry is a valid request body against the Relm API as written.

Three objects, one rule

Model each fund as a company, each partner as a contact linked to that company, and each live fundraising conversation as a deal. The rule that keeps the model clean: a fact lives on the object it stays true for. Fund size goes on the company; your thesis-fit score is specific to this raise, so it goes on the deal. Come back for the Series A and you open a fresh deal against the same company - the fund-level research is already done.

FieldOnTypeWhy it earns its place
fund_size_centscompanycurrencyA $2B fund cannot lead a $3M seed.
stage_focuscompanymultiselectFunds invest across stages; a single-value field lies. Enum: pre-seed / seed / A / B+.
check_min_cents, check_max_centscompanycurrencyNumeric, not a text range - so an agent can filter: max check below a third of your round cannot lead.
thesiscompanytextOne line, in their words. Feeds the fit score honestly.
leads_roundscompanybooleanRound math depends on leads. Ask early, record it once.
fund_rolecontactselectThe biggest fundraising self-deception is mistaking associate enthusiasm for fund interest.
thesis_fitdealnumberYour 1-5 score. Prioritize outreach by fit, not fund brand.
intro_pathdealselectEnum, not text - so you can measure which intro channel actually converts.
intro_viadealtextThe named person who made the intro. They get the thank-you note when it closes.
next_step, next_step_datedealtext, dateEvery live deal has a next step with a date. No date = stalled - enforceable by an agent.
pass_reasondealselectThe taxonomy below. The most valuable field in the kit.
pass_notesdealtextThe verbatim quote - context for when you re-read it in 14 months.
revisit_next_rounddealbooleanDecided while the conversation is fresh: does this fund belong on the next raise's list?

The built-ins carry the rest: the sponsoring partner is the deal's primary_contact_id, the target allocation is value_cents, and every meeting is an activity - set occurred_at to backdate calls logged after the fact, so momentum reports stay honest.

The pass-reason taxonomy

Most founders record passes as free text - readable, never countable. A raise of 120 funds produces 100+ passes; that is a dataset, and it answers two questions: is something broken in my positioning right now, and who do I re-approach next round, triggered by what. Nine values cover nearly every pass, each mapping to a different next-round action:

ValueWhat it usually meansNext-round action
too_earlyReal interest, wrong quarter. The highest-value pass there is.Re-approach the moment you hit the metric they named. Put that metric in pass_notes.
stage_mismatchYour round or check size sits outside their model. Your research failed, not your pitch.Fix list-building. Re-approach at the stage they actually do.
thesis_mismatchThey do not invest in your space, full stop.Drop from the list - but funds redraw theses every few years, so keep the record.
market_sizeThey do not believe the TAM path.Re-approach only with proof the wedge expands: revenue from a new segment.
portfolio_conflictA competitive holding. Rarely expires.Remove - and ask them for intros to conflict-free funds. They often will.
team_gapRarely said aloud; often coded as "too early". Listen for it.Hiring the exec they doubted is your re-approach email.
termsInterest is real, at a lower price.First call if you ever raise flat or reset.
fund_stateBetween funds, or out of dry powder. Nothing to do with you.Watch for their next fund announcement; book the meeting that week.
ghostedEngagement died silently after a real meeting.One milestone-based re-approach. If it dies again, treat as thesis_mismatch.

Two operating rules. The mid-raise signal: the same reason three or more times inside two weeks is a positioning problem - fix the deck, not the list. The post-raise ritual: the week after closing, filter passes where revisit_next_round is true - a pre-qualified next-round target list with a named trigger per fund, worth more than any investor database you can buy.

The artifact: enums + fields, copy-paste

One JSON document. Each entry in enums POSTs to /v1/enums as-is, each entry in fields POSTs to /v1/fields - enums first, so the select fields have their options. Values are then written under custom_fields on any create or update; unknown values come back 422 with the valid set, so an agent self-corrects. The registered fields and enum groups read back from GET /v1/schema; the rest of the API surface is in the docs.

{
  "enums": [
    { "group": "company.stage_focus", "value": "pre_seed",      "label": "Pre-seed" },
    { "group": "company.stage_focus", "value": "seed",          "label": "Seed" },
    { "group": "company.stage_focus", "value": "series_a",      "label": "Series A" },
    { "group": "company.stage_focus", "value": "series_b_plus", "label": "Series B+" },
    { "group": "contact.fund_role",   "value": "gp",            "label": "General Partner" },
    { "group": "contact.fund_role",   "value": "partner",       "label": "Partner" },
    { "group": "contact.fund_role",   "value": "principal",     "label": "Principal" },
    { "group": "contact.fund_role",   "value": "associate",     "label": "Associate" },
    { "group": "contact.fund_role",   "value": "platform",      "label": "Platform / non-investing" },
    { "group": "deal.intro_path",     "value": "founder_intro", "label": "Founder intro" },
    { "group": "deal.intro_path",     "value": "investor_intro","label": "Investor intro" },
    { "group": "deal.intro_path",     "value": "operator_intro","label": "Operator intro" },
    { "group": "deal.intro_path",     "value": "cold_outbound", "label": "Cold outbound" },
    { "group": "deal.intro_path",     "value": "inbound",       "label": "Inbound" },
    { "group": "deal.intro_path",     "value": "event",         "label": "Event / conference" },
    { "group": "deal.pass_reason",    "value": "too_early",     "label": "Too early / traction bar" },
    { "group": "deal.pass_reason",    "value": "stage_mismatch","label": "Stage or check mismatch" },
    { "group": "deal.pass_reason",    "value": "thesis_mismatch","label": "Thesis mismatch" },
    { "group": "deal.pass_reason",    "value": "market_size",   "label": "Market size doubt" },
    { "group": "deal.pass_reason",    "value": "portfolio_conflict","label": "Portfolio conflict" },
    { "group": "deal.pass_reason",    "value": "team_gap",      "label": "Team gap" },
    { "group": "deal.pass_reason",    "value": "terms",         "label": "Terms / valuation" },
    { "group": "deal.pass_reason",    "value": "fund_state",    "label": "Fund state / dry powder" },
    { "group": "deal.pass_reason",    "value": "ghosted",       "label": "Ghosted after engagement" }
  ],
  "fields": [
    { "object": "company", "key": "fund_size_cents",    "label": "Fund size",               "data_type": "currency" },
    { "object": "company", "key": "stage_focus",        "label": "Stage focus",             "data_type": "multiselect", "enum_group": "company.stage_focus" },
    { "object": "company", "key": "check_min_cents",    "label": "Min first check",         "data_type": "currency" },
    { "object": "company", "key": "check_max_cents",    "label": "Max first check",         "data_type": "currency" },
    { "object": "company", "key": "thesis",             "label": "Thesis (their words)",    "data_type": "text" },
    { "object": "company", "key": "leads_rounds",       "label": "Leads rounds",            "data_type": "boolean" },
    { "object": "contact", "key": "fund_role",          "label": "Role at fund",            "data_type": "select", "enum_group": "contact.fund_role" },
    { "object": "deal",    "key": "thesis_fit",         "label": "Thesis fit (1-5)",        "data_type": "number" },
    { "object": "deal",    "key": "intro_path",         "label": "Intro path",              "data_type": "select", "enum_group": "deal.intro_path" },
    { "object": "deal",    "key": "intro_via",          "label": "Intro via (person)",      "data_type": "text" },
    { "object": "deal",    "key": "next_step",          "label": "Agreed next step",        "data_type": "text" },
    { "object": "deal",    "key": "next_step_date",     "label": "Next step due",           "data_type": "date" },
    { "object": "deal",    "key": "pass_reason",        "label": "Pass reason",             "data_type": "select", "enum_group": "deal.pass_reason" },
    { "object": "deal",    "key": "pass_notes",         "label": "Pass notes (verbatim)",   "data_type": "text" },
    { "object": "deal",    "key": "revisit_next_round", "label": "Revisit next round",      "data_type": "boolean" }
  ]
}

Apply it with your agent

Connect your agent to the Relm MCP server (https://api.relmcrm.com/mcp), then paste the JSON above plus this prompt - the whole model stands up in one conversation:

Set up my fundraising data model in Relm:
1. Create every enum option in the enums array, then register
   every custom field in the fields array (enums first, so the
   select fields resolve).
2. Read back GET /v1/schema and confirm company, contact and
   deal now show the new fields with the right enum groups.
3. For each fund on the list I paste next: create a company with
   fund_size_cents / stage_focus / check_min_cents /
   check_max_cents / thesis / leads_rounds under custom_fields,
   a contact for the named partner (fund_role set, linked via
   company_id), and a deal in my fundraising pipeline with
   thesis_fit, intro_path and intro_via under custom_fields and
   the partner as primary_contact_id.
4. Rehearse in test mode, show me three sample records, then
   replay in live mode after I confirm.

Or register the centerpiece directly over REST - one enum value and the field that uses it:

curl https://api.relmcrm.com/v1/enums \
  -H "Authorization: Bearer relm_live_..." \
  -H "Content-Type: application/json" \
  -d '{"group":"deal.pass_reason","value":"too_early","label":"Too early / traction bar"}'

curl https://api.relmcrm.com/v1/fields \
  -H "Authorization: Bearer relm_live_..." \
  -H "Content-Type: application/json" \
  -d '{"object":"deal","key":"pass_reason","label":"Pass reason","data_type":"select","enum_group":"deal.pass_reason"}'

New to agent-operated CRMs? Start with how to give your AI agent a CRM. Pair this model with the fundraising pipeline template for the stages, keep the field set disciplined with the CRM hygiene checklist, and when a next-round trigger fires, run the re-engagement sequence against your revisit list.

FAQ

Why use a pass-reason taxonomy instead of free text?

Free-text pass reasons cannot be counted. A controlled vocabulary of nine values makes rejection data queryable: three thesis_mismatch passes in two weeks is a positioning problem you can fix mid-raise, and every too_early pass becomes a dated re-approach trigger for the next round. Keep a separate pass_notes text field for the verbatim quote - the enum is for analysis, the notes are for context.

Should investor data live on the company or the deal?

Facts about the fund - size, stage focus, check range, thesis - live on the company, because they are true across every conversation you will ever have with that fund. Facts about this conversation - fit score, intro path, next step, pass reason - live on the deal, because your Series A conversation with the same fund is a new deal with new answers. Partners are contacts linked to the fund.

What data type should check sizes use?

Store check_min_cents and check_max_cents as currency in integer minor units (cents), not as a text range like "$500k-2M". Numbers let an agent filter mechanically: a fund whose max check is below a third of your round cannot lead, and one whose min check exceeds your round should not be on the list. Text ranges force a human to re-read every record.

Can I apply this kit to a workspace that already has custom fields?

Yes. Both POST /v1/fields and POST /v1/enums in Relm are idempotent on name: re-registering an existing field key or enum value is a no-op that returns the current set, so running the kit twice - or on top of the fundraising pipeline template's fields - never errors or duplicates. Note that an existing field keeps its original data type.

How do I track the partner separately from the fund?

Create the partner as a contact with company_id pointing at the fund, set fund_role so you know whether you are talking to a decision-maker, and set the deal's primary_contact_id to the partner sponsoring your deal. Five associate calls at one fund is one deal with one primary contact - not five live deals.

Let an agent keep the raise data clean

1,000 requests a month on the Free plan, no card. See pricing.

Start free →