# Salesforce Client

Execute SOQL queries, CRUD operations, and bulk operations against Salesforce with full type safety and runtime validation.

## Methods

| Method                                                     | Description                                         |
| ---------------------------------------------------------- | --------------------------------------------------- |
| `query(soql, schema, metadata?)`                           | Execute a SOQL query with per-row schema validation |
| `create(resourceType, body, metadata?)`                    | Create a new Salesforce object                      |
| `read(resourceType, resourceId, schema, metadata?)`        | Read a single object by ID                          |
| `update(resourceType, resourceId, body, metadata?)`        | Update an existing object                           |
| `remove(resourceType, resourceId, metadata?)`              | Delete an object by ID                              |
| `bulkCreate(resourceType, records, metadata?)`             | Bulk create multiple objects                        |
| `bulkUpdate(resourceType, records, metadata?)`             | Bulk update multiple objects                        |
| `bulkDelete(resourceType, records, metadata?)`             | Bulk delete multiple objects                        |
| `bulkUpsert(resourceType, records, externalId, metadata?)` | Bulk upsert with external ID matching               |

## Usage

### SOQL Query

```typescript
import { api, z, salesforce } from "@superblocksteam/sdk-api";

// Integration ID from the integrations panel
const PROD_SALESFORCE = "a1b2c3d4-5678-90ab-cdef-salesfor0001";

const AccountSchema = z.object({
  Id: z.string(),
  Name: z.string(),
  Industry: z.string().nullable(),
});

export default api({
  integrations: {
    salesforce: salesforce(PROD_SALESFORCE),
  },
  name: "SalesforceExample",
  input: z.object({}),
  output: z.object({
    accounts: z.array(AccountSchema),
  }),
  async run(ctx) {
    const accounts = await ctx.integrations.salesforce.query(
      "SELECT Id, Name, Industry FROM Account LIMIT 10",
      AccountSchema,
    );

    return { accounts };
  },
});
```

### Query with Relationships

```typescript
const OpportunitySchema = z.object({
  Id: z.string(),
  Name: z.string(),
  Amount: z.number().nullable(),
  Account: z
    .object({
      Name: z.string(),
    })
    .nullable(),
});

const opportunities = await ctx.integrations.salesforce.query(
  `SELECT Id, Name, Amount, Account.Name
   FROM Opportunity
   WHERE StageName = 'Closed Won'`,
  OpportunitySchema,
);
```

### Create a Record

```typescript
await ctx.integrations.salesforce.create("Account", {
  Name: "Acme Corp",
  Industry: "Technology",
  Website: "https://acme.example.com",
});
```

### Read a Record by ID

```typescript
const account = await ctx.integrations.salesforce.read(
  "Account",
  "001xx000003DGb2AAG",
  z.object({
    Id: z.string(),
    Name: z.string(),
    Industry: z.string().nullable(),
  }),
);
```

### Update a Record

```typescript
await ctx.integrations.salesforce.update("Account", "001xx000003DGb2AAG", {
  Name: "Acme Corp (Updated)",
  Industry: "Software",
});
```

### Delete a Record

```typescript
await ctx.integrations.salesforce.remove("Account", "001xx000003DGb2AAG");
```

### Bulk Create

```typescript
await ctx.integrations.salesforce.bulkCreate("Contact", [
  { FirstName: "Alice", LastName: "Smith", Email: "alice@example.com" },
  { FirstName: "Bob", LastName: "Jones", Email: "bob@example.com" },
  { FirstName: "Charlie", LastName: "Lee", Email: "charlie@example.com" },
]);
```

### Bulk Update

```typescript
await ctx.integrations.salesforce.bulkUpdate("Contact", [
  { Id: "003xx000004TmiQAAS", Email: "alice-new@example.com" },
  { Id: "003xx000004TmiRAAS", Email: "bob-new@example.com" },
]);
```

### Bulk Delete

```typescript
await ctx.integrations.salesforce.bulkDelete("Contact", [
  { Id: "003xx000004TmiQAAS" },
  { Id: "003xx000004TmiRAAS" },
]);
```

### Bulk Upsert

```typescript
await ctx.integrations.salesforce.bulkUpsert(
  "Account",
  [
    { ExternalId__c: "EXT-001", Name: "Acme Corp", Industry: "Tech" },
    { ExternalId__c: "EXT-002", Name: "Globex Inc", Industry: "Finance" },
  ],
  "ExternalId__c",
);
```

## Trace Metadata

All methods accept an optional `metadata` parameter as the last argument for diagnostics labeling. See the [root SDK README](../../../README.md#trace-metadata) for details.

## Common Pitfalls

### Schema Must Match Salesforce Field Names

Use the exact API field names from Salesforce (case-sensitive):

```typescript
// CORRECT - Matches Salesforce API field names
const Schema = z.object({
  Id: z.string(),
  FirstName: z.string(),
  LastName: z.string(),
});

// WRONG - Using different casing
const Schema = z.object({
  id: z.string(),
  firstName: z.string(),
  lastName: z.string(),
});
```

### Nullable Fields

Many Salesforce fields can be null. Use `.nullable()`:

```typescript
const Schema = z.object({
  Id: z.string(),
  Email: z.string().nullable(),
  Phone: z.string().nullable(),
});
```

### Bulk Records Require Id for Updates/Deletes

Bulk update and delete records must include the `Id` field:

```typescript
// CORRECT
await ctx.integrations.salesforce.bulkUpdate("Contact", [
  { Id: "003xx000004TmiQAAS", Email: "new@example.com" },
]);

// WRONG - Missing Id field
await ctx.integrations.salesforce.bulkUpdate("Contact", [
  { Email: "new@example.com" },
]);
```

## Error Handling

```typescript
import {
  QueryValidationError,
  RestApiValidationError,
  IntegrationError,
} from "@superblocksteam/sdk-api";

try {
  const accounts = await ctx.integrations.salesforce.query(
    "SELECT Id, Name FROM Account",
    AccountSchema,
  );
} catch (error) {
  if (error instanceof QueryValidationError) {
    // Per-row validation failure from query()
    console.error("Row validation failed:", error.details);
  } else if (error instanceof RestApiValidationError) {
    // Validation failure from read()
    console.error("Validation failed:", error.details.zodError);
  } else if (error instanceof IntegrationError) {
    // Salesforce API error
    console.error("Salesforce error:", error.message);
  }
}
```

## API Reference

- [SOQL Reference](https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/)
- [Salesforce Object Reference](https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/)
- [Bulk API 2.0](https://developer.salesforce.com/docs/atlas.en-us.api_asynch.meta/api_asynch/)
