Keep CRM up to date
Your users in your CRM
- onUser created on WorkOS
- attioLookup if user exists on Attio
- attioUpsert the user into Attio
- customFilter users or add enrichment
- logIf the user was created or updated
durable — pauses for approvals, survives restarts
Keeping your users in sync between your application and CRM is always a challenge. Outside of a simple sync, you may want to consider:
- Filtering to keep certain users out of your CRM
- Custom enrichment logic so they land in the CRM with the right data
- Custom alerting to Slack
The template below shows how to sync your users from WorkOS to Attio and outlines where you can add custom logic.
Prerequisites
- A Terse project (
terse init) - Connected WorkOS and Attio integrations:
terse integrateand follow steps to connect each one.
The job
typescript
import { createJob, log } from "terse-sdk";
import {
AttioObject,
toolbox,
Triggers,
type WorkOSUserCreatedTriggerPayload,
} from "../terse.generated";
createJob({
name: "Create Attio user from WorkOS user",
triggers: [Triggers.workOS.onUserCreated()],
durable: true,
onTrigger: async (event) => {
switch (event.eventType) {
case "user.created":
await handleUserCreated(event.user);
break;
default:
throw event.eventType satisfies never;
}
},
});
async function handleUserCreated(user: WorkOSUser): Promise<void> {
// Option 1 - Custom filtering here: Add custom logic here to prevent user from being copied into Attio
// if desired
// Option 2 - Add user enrichment here
const fullName =
[user.firstName, user.lastName].filter(Boolean).join(" ") || undefined;
const existing = await toolbox.attio.queryRecords({
object: AttioObject.User,
filter: { user_id: user.id },
limit: 1,
});
// Durable since flag is enabled, will retry on API failures
await toolbox.attio.upsertRecord({
object: AttioObject.User,
matchingAttribute: "user_id",
records: [
{
user_id: user.id,
primary_email_address: user.email,
first_name: user.firstName,
last_name: user.lastName,
full_name: fullName,
},
],
});
// Logging can in addition post to Slack channel below
await log(
existing.count > 0
? `Updated existing Attio user for ${user.email} (WorkOS ${user.id})`
: `Created Attio user for ${user.email} (WorkOS ${user.id})`,
);
}
type WorkOSUser = WorkOSUserCreatedTriggerPayload["user"];
Apollo
PostHog