← Resources

Agency CRM Data Model (custom fields kit)

Nine custom fields that make an agency CRM tell the truth about every client - what they pay, how much work that buys, who owns them, how they feel, and where they came from - plus a hard boundary between the CRM and your delivery PM tool. Ships as copy-paste JSON.

Who this is for

Marketing, design, dev and consulting agencies - solo operators to roughly 50-client shops - setting up a CRM properly for the first time, or cleaning up one that has quietly turned into a second project-management tool. Use it before you import clients, not after: retrofitting a data model onto a thousand inconsistent records is a week of grim cleanup, while installing it on day one is 22 small API calls - or one pasted agent prompt.

The failure mode this kit prevents is specific. Agency CRMs rot in one of two directions: they hold too little (a name, an email, a vague "active" tag - so nobody can answer "which clients are underpriced?") or too much (tasks, deliverables, Figma links - so the CRM duplicates the PM tool and both go stale). The fix is a small, opinionated field set and one rule about what stays out.

The one rule: CRM is the relationship, PM is the work

A client record answers questions about the relationship: what do they pay, what did they buy, who owns them, are they happy, will they renew, where did they come from. A project answers questions about the work: what is due, who is doing it, is it blocked. Different questions, different systems, different rates of change - relationship data mutates monthly, delivery data mutates hourly. Merge them and the fast-moving data buries the slow-moving truth.

Belongs in the CRMBelongs in the PM tool
Retainer amount and engagement typeTasks, subtasks, tickets
Scope ceiling (hours per month)Time entries against those hours
Account ownerDelivery assignees
NPS and relationship healthSprint boards, statuses, deadlines
Referral source and win/loss reasonsCreative feedback, briefs, files
Renewal dates, calls, meeting notesStandup notes, retro docs

The two systems meet at exactly one point: a pm_project_url field on the company that links to the Asana, Linear or ClickUp project. One click from relationship to work; never a copy of the work inside the CRM. If a field would change more than once a week, it does not belong here.

Where each field lives

Clients outlive deals, so durable client attributes go on the company. Per-engagement facts - scope of work, contract end, loss reason - go on the deal, and that half of the model is covered by the agency client pipeline template. Contacts carry one field: their role in the account, because "decision maker" and "day-to-day" deserve different emails.

One nuance worth knowing before you commit: in Relm, automation and sequence triggers fire on contact.* and deal.* events, not on company changes. So anything you want a workflow to react to - a stage move, a status flip - should live on the deal; the company holds the reference data those workflows read. That split is not an accident of the API, it mirrors reality: workflows follow engagements, reference data follows the client.

The field kit

FieldObjectTypeWhy it exists
retainer_centscompanycurrencyCurrent monthly retainer in integer minor units. 750000 = $7,500. The single most-filtered field in an agency CRM.
project_typecompanyselectretainer / project / hybrid. Retainer clients get renewal motions; project clients get resell motions. Mixing them ruins both.
scope_hourscompanynumberThe monthly hours ceiling the retainer buys. Divide retainer by this to see your effective rate - the underpricing detector.
account_ownercompanytextOne name. The person who runs the QBR, defends the scope, and owns the renewal. Ambiguity here is how clients churn politely.
nps_latestcompanynumberMost recent 0-10 response. 9-10 promoter, 7-8 passive, 0-6 detractor - Net Promoter Score is Fred Reichheld's framework, developed at Bain & Company.
nps_updated_atcompanydateWhen that score was collected. An NPS older than a quarter is a rumor, not a signal.
referral_sourcecompanyselectreferral / inbound / outbound / partner / event / other. Aggregate this yearly and fund the channel that actually wins clients.
pm_project_urlcompanyurlThe one sanctioned bridge to the delivery world.
rolecontactselectdecision_maker / day_to_day / finance / technical. Renewal emails go to decision makers, status updates to day-to-day.

Operating cadence: retainer_cents, scope_hours and project_type update on signature or amendment only. nps_latest refreshes quarterly - log each raw survey response as a dated activity so you keep the trend, not just the latest point. referral_source is written once at creation and never edited. Naming and casing rules for all of this live in the CRM hygiene checklist; if you run a SaaS motion alongside the agency, the SaaS CRM data model is the sibling kit.

The field kit (copy-paste)

One JSON document: every select option is a valid POST /v1/enums body, every field a valid POST /v1/fields body. Create the enums first, then the fields, then write values under custom_fields. Shapes match the Relm API exactly; in any other CRM, recreate them by hand.

{
  "enum_options": [
    { "group": "company.project_type", "value": "retainer", "label": "Retainer" },
    { "group": "company.project_type", "value": "project", "label": "One-off project" },
    { "group": "company.project_type", "value": "hybrid", "label": "Hybrid" },
    { "group": "company.referral_source", "value": "referral", "label": "Referral" },
    { "group": "company.referral_source", "value": "inbound", "label": "Inbound" },
    { "group": "company.referral_source", "value": "outbound", "label": "Outbound" },
    { "group": "company.referral_source", "value": "partner", "label": "Partner" },
    { "group": "company.referral_source", "value": "event", "label": "Event" },
    { "group": "company.referral_source", "value": "other", "label": "Other" },
    { "group": "contact.client_role", "value": "decision_maker", "label": "Decision maker" },
    { "group": "contact.client_role", "value": "day_to_day", "label": "Day-to-day" },
    { "group": "contact.client_role", "value": "finance", "label": "Finance" },
    { "group": "contact.client_role", "value": "technical", "label": "Technical" }
  ],
  "fields": [
    { "object": "company", "key": "retainer_cents", "label": "Monthly retainer (cents)", "data_type": "currency" },
    { "object": "company", "key": "project_type", "label": "Engagement type", "data_type": "select", "enum_group": "company.project_type" },
    { "object": "company", "key": "scope_hours", "label": "Scope hours per month", "data_type": "number" },
    { "object": "company", "key": "account_owner", "label": "Account owner", "data_type": "text" },
    { "object": "company", "key": "nps_latest", "label": "Latest NPS response (0-10)", "data_type": "number" },
    { "object": "company", "key": "nps_updated_at", "label": "NPS last collected", "data_type": "date" },
    { "object": "company", "key": "referral_source", "label": "Referral source", "data_type": "select", "enum_group": "company.referral_source" },
    { "object": "company", "key": "pm_project_url", "label": "Delivery project URL", "data_type": "url" },
    { "object": "contact", "key": "role", "label": "Role in account", "data_type": "select", "enum_group": "contact.client_role" }
  ]
}

Values are validated against the registered types on every write: a select value outside its enum group returns a 422 with valid_options, a non-number in scope_hours fails loud, and an unregistered key under custom_fields is rejected instead of silently vivified. That strictness is what keeps the model true a year in.

Apply it with your agent

If your CRM is operable by an LLM, installation is one pasted prompt. Point an MCP client at https://api.relmcrm.com/mcp with your key:

Read my Relm schema. Then, in test mode first:
1. Create every enum option from the JSON kit below via POST /v1/enums.
2. Register the nine custom fields via POST /v1/fields - eight on company,
   one on contact - with the exact keys, data types and enum groups given.
3. Verify by re-reading the schema, then create one sample company
   ("Acme Co", retainer_cents 750000, project_type "retainer",
   scope_hours 40) to prove the fields accept values.
Show me the resulting schema, then repeat in live mode.
[paste the JSON kit here]

Or register the anchor field over REST and write a value to it:

curl https://api.relmcrm.com/v1/fields \
  -H "Authorization: Bearer relm_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "object": "company", "key": "retainer_cents",
        "label": "Monthly retainer (cents)", "data_type": "currency" }'

curl https://api.relmcrm.com/v1/companies \
  -H "Authorization: Bearer relm_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "name": "Acme Co", "domain": "acme.com",
        "custom_fields": { "retainer_cents": 750000 } }'

Full API reference in the docs. Rehearse the whole install with a relm_test_ key first - test mode is free and invisible to live data.

FAQ

Should agency client data live on the company or the deal?

Durable client attributes - retainer, scope hours, owner, NPS, referral source - live on the company, because clients outlive deals. Per-engagement facts like scope of work and contract end live on the deal. One practical exception: automation events fire on contacts and deals, not companies, so any field you want to trigger a workflow from should also exist at the deal level.

What does not belong in an agency CRM?

Tasks, tickets, deliverables, sprint boards, time entries, creative feedback and file storage. Those are delivery artifacts and they belong in the PM tool. The CRM holds the relationship layer: who the client is, what they pay, where the scope boundary sits, how they feel, and where they came from. The two systems meet at exactly one point - a pm_project_url field on the company.

How do I track NPS per client in a CRM?

Store the latest 0-10 response and its date as two company fields (nps_latest, nps_updated_at), and log every individual survey response as a dated activity on the company's primary contact. The fields give you a filterable current-state view; the activities give you the trend. A client at 6 or below with a renewal inside 90 days is your churn watchlist.

Why is the retainer stored in cents?

Integer minor units avoid floating-point drift and match how payment systems like Stripe represent money. Relm's currency field type expects a number, so 750000 means $7,500.00. Every sum, filter and report stays exact, and an agent never has to guess whether 7500 means dollars or cents - the field name says cents.

Can I use this data model without Relm?

Yes. The model is tool-agnostic: recreate the same fields as HubSpot company properties, Attio attributes or Pipedrive custom fields, and enforce the CRM-vs-PM boundary by policy. The JSON artifact is shaped for Relm's API so an AI agent can install everything in one pass, but the thinking is the product.

Install this data model in one agent turn

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

Start free →