Skip to main content

Salesforce Integration

The Salesforce integration provides comprehensive access to the Salesforce REST API, including SOQL queries, record CRUD operations, batch/composite operations, and email functionality.

Setup

You must configure Salesforce before using it in your code. Only configured integrations are available to your agent.
1

Open Integrations panel

In your spreadsheet, click Integrations in the left panel (or visit orangeslice.com/spreadsheets/<spreadsheet_id>/edits?panel=integrations)
2

Configure Salesforce

Select Salesforce and enter your instance URL and access token
3

Use in your code

Once configured, access Salesforce via integrations.salesforce

Query Operations

SOQL Query

// Basic query
const result = await integrations.salesforce.query<{
  Id: string;
  Name: string;
  Email: string;
}>(`
  SELECT Id, Name, Email 
  FROM Contact 
  WHERE Email != null 
  LIMIT 100
`);

console.log(`Total records: ${result.totalSize}`);
for (const record of result.records) {
  console.log(record.Name, record.Email);
}

Query with Pagination

let result = await integrations.salesforce.query('SELECT Id, Name FROM Account');

while (!result.done) {
  // Process current batch
  for (const record of result.records) {
    console.log(record.Name);
  }
  
  // Get next batch
  if (result.nextRecordsUrl) {
    result = await integrations.salesforce.queryMore(result.nextRecordsUrl);
  }
}

Include Deleted Records

const result = await integrations.salesforce.query(
  'SELECT Id, Name FROM Account WHERE IsDeleted = true',
  { includeDeleted: true }
);
const result = await integrations.salesforce.search(
  'FIND {John*} IN ALL FIELDS RETURNING Contact(Id, Name, Email), Lead(Id, Name, Email)'
);

for (const record of result.searchRecords) {
  console.log(record.attributes.type, record.Name);
}

Single Record Operations

Create Record

const result = await integrations.salesforce.createRecord('Contact', {
  FirstName: 'John',
  LastName: 'Doe',
  Email: '[email protected]',
  Phone: '+1-555-0123',
  AccountId: 'account-id'
});

console.log('Created:', result.id);
console.log('Success:', result.success);

Get Record

// Get all fields
const contact = await integrations.salesforce.getRecord('Contact', 'contact-id');

// Get specific fields
const contactWithFields = await integrations.salesforce.getRecord<{
  Id: string;
  FirstName: string;
  LastName: string;
  Email: string;
}>('Contact', 'contact-id', {
  fields: ['FirstName', 'LastName', 'Email']
});

Update Record

await integrations.salesforce.updateRecord('Contact', 'contact-id', {
  Phone: '+1-555-9999',
  Description: 'Updated via API'
});

Delete Record

await integrations.salesforce.deleteRecord('Contact', 'contact-id');

Upsert Record

Upsert using an external ID field:
const result = await integrations.salesforce.upsertRecord(
  'Contact',
  'External_Id__c',  // External ID field name
  'EXT-12345',       // External ID value
  {
    FirstName: 'John',
    LastName: 'Doe',
    Email: '[email protected]'
  }
);

console.log('Record ID:', result.id);
console.log('Created:', result.created ?? 'updated');

Batch/Collection Operations

Create Multiple Records

const results = await integrations.salesforce.createRecords({
  allOrNone: false, // Continue on partial failures
  records: [
    { attributes: { type: 'Contact' }, FirstName: 'John', LastName: 'Doe', Email: '[email protected]' },
    { attributes: { type: 'Contact' }, FirstName: 'Jane', LastName: 'Doe', Email: '[email protected]' },
    { attributes: { type: 'Contact' }, FirstName: 'Bob', LastName: 'Smith', Email: '[email protected]' }
  ]
});

for (const result of results) {
  if (result.success) {
    console.log('Created:', result.id);
  } else {
    console.error('Failed:', result.errors);
  }
}

Retrieve Multiple Records

const contacts = await integrations.salesforce.retrieveRecords<{
  Id: string;
  FirstName: string;
  LastName: string;
  Email: string;
}>({
  sobject: 'Contact',
  ids: ['id1', 'id2', 'id3'],
  fields: ['FirstName', 'LastName', 'Email']
});

Update Multiple Records

const results = await integrations.salesforce.updateRecords({
  allOrNone: true, // All must succeed
  records: [
    { Id: 'id1', attributes: { type: 'Contact' }, Phone: '111-111-1111' },
    { Id: 'id2', attributes: { type: 'Contact' }, Phone: '222-222-2222' }
  ]
});

Delete Multiple Records

const results = await integrations.salesforce.deleteRecords({
  ids: ['id1', 'id2', 'id3'],
  allOrNone: false
});

Upsert Multiple Records

const results = await integrations.salesforce.upsertRecords({
  sobject: 'Contact',
  externalIdField: 'External_Id__c',
  allOrNone: false,
  records: [
    { External_Id__c: 'EXT-001', FirstName: 'John', LastName: 'Doe' },
    { External_Id__c: 'EXT-002', FirstName: 'Jane', LastName: 'Smith' }
  ]
});

Metadata Operations

Describe Global

List all available SObjects:
const describe = await integrations.salesforce.describeGlobal();

console.log(`Max batch size: ${describe.maxBatchSize}`);

for (const sobject of describe.sobjects) {
  if (sobject.queryable) {
    console.log(`${sobject.name} (${sobject.label})`);
  }
}

Describe SObject

Get detailed metadata for a specific object:
const describe = await integrations.salesforce.describeSObject('Contact');

console.log(`Object: ${describe.label}`);
console.log(`Key Prefix: ${describe.keyPrefix}`);

// List fields
for (const field of describe.fields) {
  console.log(`${field.name}: ${field.type} (${field.label})`);
  
  if (field.picklistValues?.length) {
    console.log('  Values:', field.picklistValues.map(p => p.value));
  }
}

// List relationships
for (const rel of describe.childRelationships) {
  console.log(`Child: ${rel.childSObject} via ${rel.field}`);
}

Email Operations

Send Email

const result = await integrations.salesforce.sendEmail({
  emailAddresses: '[email protected]',
  emailSubject: 'Hello from Salesforce',
  emailBody: 'This is a test email sent via the Salesforce API.',
  senderType: 'CurrentUser'
});

if (result.isSuccess) {
  console.log('Email sent successfully');
} else {
  console.error('Failed:', result.errors);
}

Send Email as Org-Wide Address

const result = await integrations.salesforce.sendEmail({
  emailAddresses: '[email protected], [email protected]',
  emailSubject: 'Company Newsletter',
  emailBody: 'Monthly newsletter content...',
  senderType: 'OrgWideEmailAddress',
  orgWideEmailAddressId: 'org-wide-email-id'
});

Sender Types

TypeDescription
CurrentUserSend as the authenticated user (default)
DefaultWorkflowUserSend as the org’s default workflow user
OrgWideEmailAddressSend from an org-wide email address

Types Reference

SalesforceRecord

interface SalesforceRecord {
  Id: string;
  attributes?: {
    type: string;
    url: string;
  };
  [key: string]: unknown;
}

SalesforceQueryResult

interface SalesforceQueryResult<T = SalesforceRecord> {
  totalSize: number;
  done: boolean;
  records: T[];
  nextRecordsUrl?: string;
}

SalesforceCreateResult

interface SalesforceCreateResult {
  id: string;
  success: boolean;
  errors: SalesforceError[];
}

SalesforceError

interface SalesforceError {
  statusCode?: string;
  message?: string;
  fields?: string[];
}

SalesforceField

interface SalesforceField {
  name: string;
  label: string;
  type: string;
  length?: number;
  precision?: number;
  scale?: number;
  nillable: boolean;
  createable: boolean;
  updateable: boolean;
  defaultValue?: unknown;
  picklistValues?: Array<{
    active: boolean;
    label: string;
    value: string;
    defaultValue: boolean;
  }>;
  referenceTo?: string[];
  relationshipName?: string;
  externalId?: boolean;
  unique?: boolean;
  calculated?: boolean;
  custom?: boolean;
}

SendEmailInput

interface SendEmailInput {
  /** Email address(es) - single or comma-separated */
  emailAddresses: string;
  /** The email subject line */
  emailSubject: string;
  /** The plain text email body */
  emailBody: string;
  /** Who to send as */
  senderType?: 'CurrentUser' | 'DefaultWorkflowUser' | 'OrgWideEmailAddress';
  /** Required for OrgWideEmailAddress */
  orgWideEmailAddressId?: string;
  /** Optional reply-to address */
  replyTo?: string;
}

Common SOQL Patterns

Join Queries

// Get Contacts with Account info
const result = await integrations.salesforce.query(`
  SELECT Id, Name, Email, Account.Name, Account.Industry
  FROM Contact
  WHERE Account.Industry = 'Technology'
`);

Aggregate Queries

const result = await integrations.salesforce.query(`
  SELECT COUNT(Id) total, StageName
  FROM Opportunity
  GROUP BY StageName
`);

Date Filters

const result = await integrations.salesforce.query(`
  SELECT Id, Name, CreatedDate
  FROM Lead
  WHERE CreatedDate = LAST_N_DAYS:30
`);

Relationship Queries

// Parent to child (subquery)
const result = await integrations.salesforce.query(`
  SELECT Id, Name, (SELECT Id, FirstName, LastName FROM Contacts)
  FROM Account
  WHERE Id = 'account-id'
`);