SaaS and technology · Reverse ETL

Product usage in the CRM

Each account's plan, payment status and usage written into the CRM where sales and CS work, only when they change, without duplicating contacts or erasing what the team filled in.

UsageCommon
EffortMedium
ForSaaS companies whose sales and CS teams work in the CRM, especially with a trial or self-service.
Source

Product, billing and single-customer map

Rule

Only what changed, only allowed fields, with locks

Outcome

Properties and associations in the CRM

Activation

Lists, tasks and alerts in the CRM itself

What it solves

  • Reps and CSMs see usage, plan and payment without leaving the CRM.
  • CRM lists and workflows start firing on real product behavior.
  • The sync does not duplicate contacts or overwrite what the team filled in.

Business questions it answers

Ask your agent these questions once the case is live.

Which trial accounts are using the product and have not talked to sales yet?
Which paying customers have not logged in for more than 30 days?
Which accounts are behind on payment, and who owns them?
How many active users does each customer in my book have?
Which self-service workspaces look ready for an upsell?

What you need

  • Product database or events and billing connected to Nekt
  • The single-customer map with each record's CRM id
  • The list of properties the routine is allowed to write

What you get

  • Usage, plan and payment properties kept current in the CRM
  • Contact, company and deal associations to the product workspace
  • A send log per record, with result and error
  • CRM lists, tasks and alerts fed by product data

Where to activate

CRM lists and workflows

A trial with usage and no contact becomes a sales task; a paying customer with no login for 30 days becomes a CS task.

Support and CS tools

Plan, status and exit date in the support tool, so support knows who they are talking to.

Ad audiences

CRM users and states become lookalike audiences and exclusion lists on the ad platforms.

Ready to build?

The calculation is simple. The care goes into not duplicating, not overwriting and locking before writing. Start in dry-run mode, with few properties. The detailed view has the business rules, the pitfalls and the acceptance test. Or send everything straight to your agent with the Nekt MCP.

The problem

Sales and CS live in the CRM, but usage, plan and payment status sit in the product database and in billing. The first sync tends to create duplicate contacts, overwrite fields the rep filled in and count internal users' activity as customer usage. Sometimes it brings down the CRM source itself. And the destination writes properties but does not create associations between objects.

How it is usually done, and the rule

Each row is a mistake that already happened in practice.

The common wayThe rule
01
Matching the product company to the CRM company by name inside the send routine.
One table per object, with the CRM id

Build one output table per CRM object (company, contact, workspace as a custom object), with the CRM record id on every row. The id comes from the single-customer map, never from an on-the-fly name match. Contacts match by email, lowercased and trimmed.

02
A single routine that creates when it does not find a match.
Create and update are separate routines

Separate records that do not exist in the CRM yet (create) from those that do (update). The update routine never creates. The create routine first checks whether the email or domain already exists. Start creation with a small pilot (for example, 15 records) and check the record is still there a few seconds later: CRM anti-duplicate automations have deleted freshly created contacts.

03
Resending every property of every record on each run.
Send only what changed

Compare the calculated value with the current CRM value and send only the difference. Example: set active user to No only for people who are Yes in the CRM and became inactive in the product. Sending everything every day burns API quota, fires workflows for nothing and pollutes the property history.

04
Overwriting phone and owner with what came from the product.
Team fields are protected

Declare which properties the routine may write. Phone, email and tax ID are filled only when empty. Owner, stage and fields the team edits are never overwritten. Properties written by the routine get their own prefix (for example, lake_ or product_), so nobody edits them by hand.

05
Counting support logins inside the customer's account.
Usage belongs to the customer, not your team

Remove internal, support and service users (by role, email domain or list), test accounts and demo accounts from the calculation. A user enabled in a company with no current package or subscription is not an active user.

06
Sending the status of the most recently created subscription.
Payment comes from billing, at customer grain

Plan, subscription status (active, past_due, canceled, trialing), MRR and first payment date come from billing, consolidated per customer and not per subscription. With several subscriptions, a declared priority rule picks the status. The first payment date is the date of the first paid charge, not the billing cycle date. Send the data's reference date along with it.

07
Creating one property for every product event.
Few properties, ready for action

Write indicators the team actually uses (active users in 7 and 30 days, days since last login, modules in use, customer tier), not one field per event or feature. Picklist values are translated and come from a closed list. HubSpot caps a property at 512,000 bytes, and the contacts source fails above 10,000 nested fields; after 3 consecutive failures, it disables itself.

08
Expecting the destination to create associations.
Associations are a separate routine

The Nekt destination writes properties by object id, but it does not associate contact to workspace, company to workspace or deal to workspace. Do that in a notebook that compares expected associations with existing ones and creates only the missing ones, with the association label. Run a dry run first: in a real case, two weeks of dry run showed hundreds of associations to create and more than a hundred contacts that did not even exist yet.

09
Running straight in production without checking the account or the volume.
Lock before writing

The routine checks the target portal or account before writing (sandbox in testing, the expected id in production) and stops if it does not match. It also checks volume: a catalog below the expected minimum or an unusual number of changes means broken source data, and the routine stops instead of deleting options or blanking fields. Every send goes to a log with record, value, result and error. API keys live in the secrets vault, never in code.

10
Filtering to active accounts only before sending.
Accounts that left are sent too

Include inactive and deleted accounts in the send, so status and exit date are updated in the CRM and in the support tool. Otherwise CS keeps seeing as active someone who already canceled.

What goes to the CRM, and where it comes from

Properties written by the routine, with their own prefix and a reference date.

Billing, consolidated per customerPlan and subscription statussubscription.status
MRR tableCustomer's current MRR
First paid chargeFirst payment datecharge.status = succeeded
Product events, no internal usersActive users in 7 and 30 days
Last login of any userDays since last login
Closed list of modulesModules in use
Cumulative volume in bandsCustomer tier
Custom object with the product idAssociated workspace

Definitions

Active user 30d
customer user, excluding internal, support and service users, with at least one login or event in the last 30 days up to the previous day
Active user in the CRM
Yes if the user is active in the product and the company has a current package or subscription; No otherwise
Days since last login
days between the last login of any of the customer's users and the reference date
Payment status
status of the customer's main subscription in billing on the reference date, chosen by a declared priority rule
First payment date
date of the customer's first paid charge with an amount above zero
Reference date
day of the data that produced the value, sent along with the property
Send batch
records whose calculated value differs from the current CRM value, with the result stored in the log

Pitfalls that already happened

Contact created and deleted right after

A CRM anti-duplicate automation deleted contacts the routine had just created, and the batch looked successful.

Create a pilot and check the record still exists a few seconds later.

CRM source disabled

Too many contact properties pushed past the 10,000 nested-field limit; extraction failed three times and the source disabled itself without warning.

Few properties, a review of unused ones and a source failure alert.

Duplicate contacts on the first load

Without separating create from update, every run recreated people whose email differed only in letter case.

Lowercase email and a create routine that checks for existence.

Canceled showing as active

Deleted companies were left out of the send and stayed active in the support tool.

Send inactive and deleted accounts with the exit date.

The team inflated customer usage

Users of a service run by the team itself worked inside customers' accounts and counted as their active users.

Exclude by user role, as well as email domain and test accounts.

API key in the code

CRM and other tool tokens were written inside notebooks, visible to anyone who opened or versioned the code.

Store them in the Nekt secrets vault and read them with nekt.load_secret.

Acceptance test

The case is done when every item passes.

Agent package

Paste into Claude, ChatGPT or any client with the Nekt MCP connected.

Full Markdown
Product usage in the CRM
- Properties written by the routine have the prefix <prefix> and carry the data's reference date. The CRM value may lag the product by up to 1 day.
- Active user = customer user with a login or event in the last 30 days, excluding internal, support, service and test accounts, in a company with a current plan.
- Payment status comes from billing, consolidated per customer: active, past_due, canceled, trialing. Past_due is not canceled.
- First payment date = first paid charge with an amount above zero, not the billing cycle date.
- The routine never overwrites owner, stage or team-edited fields. Phone, email and tax ID are filled only when empty.
- Contact, company and deal associations to the workspace are created by a dedicated routine; a record without an association is not a customer without usage.
- If the CRM value differs from the product, the source of truth is the product or billing, not the CRM.
Client exceptions:
- <client>: users with role <role> are internal and do not count as active.
- <client>: customer tier by cumulative volume: <bands>.

Variations

Spreadsheet as a bridge

A spreadsheet fed by Nekt is the single source read by an automation (for example, n8n) that distributes to several tools.

Tier by volume

Customer tier by cumulative volume (for example, by TPV) sent to the CRM and to the team's task tool.

Value per person

In self-service, one row per email with total paid and the status of the latest subscription, written to the contact.