Skip to main content

Overview

Many platforms collect valuable customer information - contact details, company data, deal information, and interaction history. Your users need this data in their CRM systems, but manually copying information between platforms is tedious and error-prone. The Traditional Problem:
  • Users must manually export data from your platform
  • Copy and paste into their CRM (HubSpot, Salesforce, Copper, etc.)
  • Risk of data entry errors and outdated information
  • No real-time sync between systems
The Alloy Automation Solution: With Alloy Automation, you can add a “Send to CRM” button to your platform that:
  • Lets users choose their preferred CRM
  • Handles authentication automatically
  • Maps your data fields to their CRM’s format
  • Pushes data with one click
  • Works with dozens of CRM platforms

Components of CRM Data Sync

  • Connectors - Pre-built integrations for CRM platforms (HubSpot, Salesforce, Copper, Zoho, Pipedrive, and more)
  • Credentials - Secure authentication that Alloy manages - OAuth flows, API keys, tokens - handled automatically
  • Actions - Operations available for each CRM (createContact, createDeal, createCompany, updateContact, etc.)
  • Field Mapping - Translation layer between your universal data model and each CRM’s specific field names and requirements

How CRM Data Sync Works

  1. User clicks “Send to CRM” in your application
  2. First time only: User selects their CRM and authenticates (OAuth handled by Alloy)
  3. Your app discovers available actions and fields for their chosen CRM
  4. Map your data to their CRM’s field format
  5. Show preview of what will be created (optional but recommended)
  6. Execute action to push data to their CRM
  7. Confirm success and store the CRM record ID for future updates
This pattern works for contacts, deals, companies, or any CRM entity. You can trigger syncs manually (button click), automatically (on record creation), in bulk (historical import), or bidirectionally (pull data from CRM back into your platform).

Building Your First Integration

Select the appropriate drop-down below based on which Alloy Automation product you’ll be building with.

Prerequisites

Before you begin, ensure you have:
  • Alloy API Key - Generate in Alloy Dashboard → Settings → API Keys
  • Base URL - https://production.runalloy.com
  • Required headers for all API calls:
Authorization: Bearer YOUR_API_KEY
x-api-version: 2025-09
x-alloy-userid: USER_ID
Store your API key securely (environment variables, secrets manager). Never expose it in client-side code.

Step 1: Create a User (One-Time Setup)

Before managing credentials or executing actions, create a user record in Alloy for each of your end-users. This enables multi-tenancy and isolates each user’s credentials and data.API Call:
POST https://production.runalloy.com/users
Authorization: Bearer YOUR_API_KEY
x-api-version: 2025-09
Content-Type: application/json

{
  "fullName": "Dexter Morgan",
  "username": "user_bhb"
}
Response:
{
  "userId": "675987329f2bda83f0dff233"
}
Store this userId in your database associated with the user’s account. You’ll pass it in the x-alloy-userid header for all subsequent API calls associated with this user.

Step 2: Discover Available CRM Connectors

List all available connectors and filter for CRM platforms. This lets you show users which CRMs they can connect to.API Call:
GET https://production.runalloy.com/connectors
Authorization: Bearer YOUR_API_KEY
x-api-version: 2025-09
Response:
{
  "connectors": [
    {
      "id": "hubspot",
      "name": "Hubspot",
      "icon": "https://cdn.runalloy.com/icons/hubspot.png",
      "group": ["input"],
      "category": ["crm"]
    },
    {
      "id": "salesforceCRM",
      "name": "Salesforce CRM",
      "icon": "https://cdn.runalloy.com/icons/salesforceCRM.png",
      "group": ["output"],
      "category": ["sales-crm"]
    }
  ]
}
Filter for CRM Connectors:
const response = await fetch('https://production.runalloy.com/connectors', {
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'x-api-version': '2025-09'
  }
});

const { connectors } = await response.json();

// Filter for CRM category
const crmConnectors = connectors.filter(connector => 
  connector.category && 
  connector.category.some(cat => cat.toLowerCase().includes('crm'))
);

// Display these to your user as options
console.log(`Available CRMs: ${crmConnectors.map(c => c.name).join(', ')}`);

Step 3: Let User Select CRM and Authenticate

When a user selects their CRM, check if they already have credentials. If not, initiate the authentication flow.Check for Existing Credentials:
GET https://production.runalloy.com/connectors/hubspot/credentials
Authorization: Bearer YOUR_API_KEY
x-api-version: 2025-09
x-alloy-userid: 675987329f2bda83f0dff233
Response (No Credentials):
{
  "credentials": []
}
Create Credential (OAuth Flow):
POST https://production.runalloy.com/connectors/hubspot/credentials
Authorization: Bearer YOUR_API_KEY
x-api-version: 2025-09
x-alloy-userid: 675987329f2bda83f0dff233
Content-Type: application/json

{
  "authenticationType": "oauth2",
  "redirectUri": "https://yourplatform.com/crm/callback"
}
Response:
{
  "oauthUrl": "https://app.hubspot.com/oauth/authorize?client_id=..."
}
Redirect your user to the oauthUrl. After they authorize, Alloy redirects them back to your redirectUri with a success parameter. The credential is now stored securely and you can use the credentialId for all future API calls.Response (Existing Credentials):
{
  "credentials": [
    {
      "credentialId": "cred_xyz789abc",
      "name": "Dexter Morgan's Hubspot",
      "type": "hubspot-oauth2",
      "createdAt": "2025-02-05T15:09:29.551Z",
      "updatedAt": "2025-05-16T10:32:58.003Z"
    }
  ]
}
Store the credentialId in your database associated with the user and their selected CRM.

Step 4: Discover Available Actions and Fields

Before pushing data, inspect what actions are available and what fields they require. This allows you to dynamically build your field mapping.List Available Resources and Actions:
GET https://production.runalloy.com/connectors/hubspot/resources
Authorization: Bearer YOUR_API_KEY
x-api-version: 2025-09
Response:
{
  "resources": [
    {
      "name": "companies",
      "description": "Manage companies.",
      "actions": [
        {
          "id": "createCompany",
          "name": "Create a company",
          "description": "Create a single company."
        },
        {
          "id": "updateCompany",
          "name": "Update a company",
          "description": "Update a company."
        }
      ]
    },
    {
      "name": "contacts",
      "description": "Manage contacts.",
      "actions": [
        {
          "id": "createContact",
          "name": "Create a contact",
          "description": "Create a new contact record."
        },
        {
          "id": "updateContact",
          "name": "Update a contact",
          "description": "Update an existing contact record by its ID."
        }
      ]
    }
  ]
}
Get Detailed Action Schema:
GET https://production.runalloy.com/connectors/hubspot/actions/createContact
Authorization: Bearer YOUR_API_KEY
x-api-version: 2025-09
Response:
{
  "action": {
    "id": "createContact",
    "displayName": "Create a contact",
    "description": "Create a new contact record.",
    "httpMethod": "post",
    "path": "/crm/v3/objects/contacts",
    "parameters": [],
    "requestBody": {
      "type": "object",
      "required": ["properties"],
      "properties": {
        "properties": {
          "type": "object",
          "required": ["firstname", "lastname", "email"],
          "properties": {
            "firstname": { "type": "string" },
            "lastname": { "type": "string" },
            "email": { "type": "string" },
            "company": { "type": "string" },
            "website": { "type": "string" },
            "phone": { "type": "string" }
          }
        }
      }
    }
  }
}
This tells you exactly what fields the CRM expects, allowing you to map your data correctly.

Step 5: Map Your Data to CRM Fields

Create a mapping between your platform’s field names and each CRM’s requirements. Different CRMs use different field names for the same data.Your Universal Data Model:
{
  "contact_email": "john.doe@acme.com",
  "contact_first_name": "John",
  "contact_last_name": "Doe",
  "contact_phone": "+1 (555) 123-4567",
  "contact_company": "Acme Corp",
  "contact_title": "Sales Manager"
}
Field Mapping Configuration:
const fieldMappings = {
  hubspot: {
    contact_email: 'email',
    contact_first_name: 'firstname',
    contact_last_name: 'lastname',
    contact_phone: 'phone',
    contact_company: 'company',
    contact_title: 'jobtitle'
  },
  salesforceCRM: {
    contact_email: 'Email',
    contact_first_name: 'FirstName',
    contact_last_name: 'LastName',
    contact_phone: 'Phone',
    contact_company: 'Company',
    contact_title: 'Title'
  },
  copper: {
    contact_email: 'emails',
    contact_first_name: 'name',
    contact_last_name: 'name',
    contact_phone: 'phone_numbers',
    contact_company: 'company_name'
  }
};

function mapFieldsForCRM(universalData, crmName) {
  const mapping = fieldMappings[crmName] || {};
  const mappedData = {};
  
  Object.entries(universalData).forEach(([key, value]) => {
    if (value) {
      const mappedKey = mapping[key] || key;
      mappedData[mappedKey] = value;
    }
  });
  
  return mappedData;
}
Example Usage:
const userData = {
  contact_email: "john.doe@acme.com",
  contact_first_name: "John",
  contact_last_name: "Doe",
  contact_phone: "+1 (555) 123-4567"
};

const hubspotData = mapFieldsForCRM(userData, 'hubspot');
// Result: { email: "john.doe@acme.com", firstname: "John", lastname: "Doe", phone: "+1 (555) 123-4567" }

const salesforceData = mapFieldsForCRM(userData, 'salesforceCRM');
// Result: { Email: "john.doe@acme.com", FirstName: "John", LastName: "Doe", Phone: "+1 (555) 123-4567" }

Step 6: Execute Action to Push Data

Now you’re ready to execute the action and push data to the user’s CRM.API Call:
POST https://production.runalloy.com/connectors/hubspot/actions/createContact/execute
Authorization: Bearer YOUR_API_KEY
x-api-version: 2025-09
x-alloy-userid: 675987329f2bda83f0dff233
Content-Type: application/json

{
  "credentialId": "cred_xyz789abc",
  "requestBody": {
    "properties": {
      "email": "john.doe@acme.com",
      "firstname": "John",
      "lastname": "Doe",
      "phone": "+1 (555) 123-4567",
      "company": "Acme Corp",
      "jobtitle": "Sales Manager"
    }
  }
}
Success Response:
{
  "responseData": {
    "id": "166643419989",
    "properties": {
      "createdate": "2025-10-23T18:52:29.567Z",
      "email": "john.doe@acme.com",
      "firstname": "John",
      "lastname": "Doe",
      "phone": "+1 (555) 123-4567",
      "company": "Acme Corp",
      "jobtitle": "Sales Manager"
    },
    "createdAt": "2025-10-23T18:52:29.567Z",
    "updatedAt": "2025-10-23T18:52:29.567Z",
    "url": "https://app.hubspot.com/contacts/166643419989"
  },
  "statusCode": 200
}
Store the returned id in your database if you need to update this contact later.Error Response:
{
  "error": {
    "code": "INVALID_CREDENTIAL",
    "message": "The credential has expired or been revoked",
    "details": "User needs to re-authenticate"
  }
}

Error Handling

All Connectivity API endpoints return structured error responses. Handle common scenarios gracefully:Credential Expired or Revoked:
{
  "error": {
    "code": "INVALID_CREDENTIAL",
    "message": "The credential has expired or been revoked"
  }
}
Solution: Prompt user to re-authenticate with their CRM.Rate Limit Exceeded:
{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Too many requests"
  }
}
Solution: Implement exponential backoff and retry logic. Wait before retrying the request.Invalid Field Value:
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid field value",
    "details": {
      "field": "email",
      "reason": "Email format is invalid"
    }
  }
}
Solution: Validate data before sending to CRM. Show user-friendly error messages.Best Practices:
  • Log execution IDs for debugging
  • Show human-friendly messages to users
  • Implement retry logic for transient failures (429, 5xx errors)
  • Store credential status in your database to avoid repeated auth failures

Connector-Specific Walkthroughs

HubSpot is one of the most popular CRM platforms, offering marketing, sales, and service tools.Available Actions:
  • createContact - Create a new contact
  • updateContact - Update existing contact
  • createDeal - Create a sales opportunity
  • createCompany - Create a company record
Authentication:
  • Type: OAuth 2.0
Field Mapping for Contacts:
{
  contact_email: 'email',           // Required
  contact_first_name: 'firstname',
  contact_last_name: 'lastname',
  contact_phone: 'phone',
  contact_company: 'company',
  contact_title: 'jobtitle',
  contact_website: 'website',
  contact_address_city: 'city',
  contact_address_state: 'state',
  contact_address_postal: 'zip',
  contact_address_street: 'address',
  contact_address_country: 'country'
}
Field Mapping for Deals:
{
  deal_name: 'dealname',           // Required
  deal_amount: 'amount',
  deal_stage: 'dealstage',
  deal_close_date: 'closedate',
  deal_probability: 'deal_probability'
}
Example: Create Contact
POST https://production.runalloy.com/connectors/hubspot/actions/createContact/execute
Authorization: Bearer YOUR_API_KEY
x-api-version: 2025-09
x-alloy-userid: user_abc123xyz
Content-Type: application/json

{
  "credentialId": "cred_xyz789abc",
  "requestBody": {
    "properties": {
      "email": "sarah.jones@techcorp.com",
      "firstname": "Sarah",
      "lastname": "Jones",
      "phone": "+1 (555) 987-6543",
      "company": "TechCorp",
      "jobtitle": "VP of Sales",
      "city": "San Francisco",
      "state": "CA"
    }
  }
}
Example: Create Deal
POST https://production.runalloy.com/connectors/hubspot/actions/createDeal/execute
Authorization: Bearer YOUR_API_KEY
x-api-version: 2025-09
x-alloy-userid: user_abc123xyz
Content-Type: application/json

{
  "credentialId": "cred_xyz789abc",
  "requestBody": {
    "properties": {
      "dealname": "Q4 Enterprise Contract",
      "amount": "75000",
      "dealstage": "negotiation",
      "closedate": "2025-12-31"
    }
  }
}
Salesforce CRM is the leading enterprise CRM platform with extensive customization capabilities.Available Actions:
  • createContact - Create a new contact
  • updateContact - Update existing contact
  • createOpportunity - Create a sales opportunity (called “Deal” in other CRMs)
  • createAccount - Create an account record (called “Company” in other CRMs)
Authentication:
  • Type: OAuth 2.0
Field Mapping for Contacts:
{
  contact_email: 'Email',           // Required
  contact_first_name: 'FirstName',
  contact_last_name: 'LastName',    // Required
  contact_phone: 'Phone',
  contact_company: 'Company',
  contact_title: 'Title',
  contact_address_street: 'MailingStreet',
  contact_address_city: 'MailingCity',
  contact_address_state: 'MailingState',
  contact_address_postal: 'MailingPostalCode',
  contact_address_country: 'MailingCountry'
}
Field Mapping for Opportunities (Deals):
{
  deal_name: 'Name',               // Required
  deal_amount: 'Amount',
  deal_stage: 'StageName',         // Required
  deal_close_date: 'CloseDate',    // Required (format: YYYY-MM-DD)
  deal_probability: 'Probability'
}
Example: Create Contact
POST https://production.runalloy.com/connectors/salesforceCRM/actions/createContact/execute
Authorization: Bearer YOUR_API_KEY
x-api-version: 2025-09
x-alloy-userid: user_abc123xyz
Content-Type: application/json

{
  "credentialId": "cred_abc456def",
  "requestBody": {
    "Email": "mike.chen@innovate.io",
    "FirstName": "Mike",
    "LastName": "Chen",
    "Phone": "+1 (555) 234-5678",
    "Title": "Director of Engineering",
    "MailingCity": "Seattle",
    "MailingState": "WA"
  }
}
Example: Create Opportunity
POST https://production.runalloy.com/connectors/salesforceCRM/actions/createOpportunity/execute
Authorization: Bearer YOUR_API_KEY
x-api-version: 2025-09
x-alloy-userid: user_abc123xyz
Content-Type: application/json

{
  "credentialId": "cred_abc456def",
  "requestBody": {
    "Name": "Enterprise Platform Migration",
    "Amount": 125000,
    "StageName": "Proposal/Price Quote",
    "CloseDate": "2025-11-30",
    "Probability": 60
  }
}
The StageName values depend on your Salesforce org’s configuration. Common values include: “Prospecting”, “Qualification”, “Proposal/Price Quote”, “Negotiation/Review”, “Closed Won”, “Closed Lost”.
Copper (formerly ProsperWorks) is a CRM designed for Google Workspace users.Available Actions:
  • createPerson - Create a new person (called “Contact” in other CRMs)
  • updatePerson - Update existing person
  • createOpportunity - Create a sales opportunity
  • createCompany - Create a company record
Authentication:
  • Type: OAuth 2.0
Field Mapping for People (Contacts):
{
  contact_email: 'emails',          // Array: [{ email: "...", category: "work" }]
  contact_first_name: 'name',       // Combined into single name field
  contact_last_name: 'name',        // Combined into single name field
  contact_phone: 'phone_numbers',   // Array: [{ number: "...", category: "work" }]
  contact_company: 'company_name',
  contact_title: 'title'
}
Field Mapping for Opportunities (Deals):
{
  deal_name: 'name',                // Required
  deal_amount: 'monetary_value',    // Integer (cents)
  deal_stage: 'pipeline_stage_id', // Must be valid stage ID
  deal_close_date: 'close_date'     // Unix timestamp
}
Example: Create Person
POST https://production.runalloy.com/connectors/copper/actions/createPerson/execute
Authorization: Bearer YOUR_API_KEY
x-api-version: 2025-09
x-alloy-userid: user_abc123xyz
Content-Type: application/json

{
  "credentialId": "cred_def789ghi",
  "requestBody": {
    "name": "Lisa Rodriguez",
    "emails": [
      {
        "email": "lisa.rodriguez@globalco.com",
        "category": "work"
      }
    ],
    "phone_numbers": [
      {
        "number": "+1 (555) 345-6789",
        "category": "work"
      }
    ],
    "title": "Product Manager",
    "company_name": "GlobalCo"
  }
}
Example: Create Opportunity
POST https://production.runalloy.com/connectors/copper/actions/createOpportunity/execute
Authorization: Bearer YOUR_API_KEY
x-api-version: 2025-09
x-alloy-userid: user_abc123xyz
Content-Type: application/json

{
  "credentialId": "cred_def789ghi",
  "requestBody": {
    "name": "Annual Subscription Renewal",
    "monetary_value": 4500000,
    "pipeline_stage_id": 123456,
    "close_date": 1735689600
  }
}
The monetary_value is in cents (so 4500000 = $45,000), and pipeline_stage_id must reference a valid pipeline stage in the user’s Copper account. You may need to fetch available pipeline stages first.

Common Integration Patterns

While this blueprint focuses on the “Send to CRM” button use case, Alloy Automation is flexible enough to support many patterns: Manual Sync (Button Click)
User reviews data and clicks “Send to CRM” to push selected records. This gives users control over what gets synced and when.
Automatic Background Sync
Trigger syncs automatically when records are created or updated in your platform. Users don’t need to take any action - data flows seamlessly.
Bulk Historical Import
When a user first connects their CRM, offer to sync all existing records from your platform. Process in batches to respect rate limits.
Bidirectional Sync
Pull data from the CRM back into your platform. Keep both systems in sync automatically.
Selective Sync with Filters
Let users choose which types of records or which customers to sync based on tags, status, or custom criteria.

Additional Resources

Next Steps:
Now that you understand CRM data sync, explore our companion blueprints on Business Loan Underwriting for financial data extraction and Journal Entries for accounting reconciliation workflows.