Skill: ivangdavila/salesforce-api-integration (version 1.0.1)

--- name: Salesforce API Integration slug: salesforce-api-integration version: 1.0.1 homepage: https://clawic.com/skills/salesforce-api-integration description: Complete Salesforce REST API for SOQL queries, CRUD operations, Bulk API, Composite API, authentication, and standard objects with error handling. changelog: Fixed memory template to use standard status values and natural language context. metadata: {"clawdbot":{"emoji":"☁️","requires":{"env":["SF_ACCESS_TOKEN","SF_INSTANCE_URL"],"config":["~/salesforce-api-integration/"]},"primaryEnv":"SF_ACCESS_TOKEN","os":["linux","darwin","win32"]}} --- # Salesforce API Integration Complete Salesforce REST API reference. See auxiliary files for detailed operations. ## Quick Start ```bash curl "$SF_INSTANCE_URL/services/data/v59.0/sobjects/" \ -H "Authorization: Bearer $SF_ACCESS_TOKEN" ``` ## Setup On first use, read `setup.md`. Preferences stored in `~/salesforce-api-integration/memory.md`. ## When to Use Any Salesforce operation: SOQL queries, record CRUD, bulk imports/exports, metadata, composite requests. ## Architecture ``` ~/salesforce-api-integration/ ├── memory.md # Org context and object schemas └── queries.md # Saved SOQL queries ``` ## Quick Reference | Topic | File | |-------|------| | Setup and authentication | `setup.md`, `memory-template.md` | | SOQL queries and search | `soql.md` | | Records: create, read, update, delete | `records.md` | | Standard objects reference | `objects.md` | | Bulk API 2.0 for large datasets | `bulk.md` | | Composite and batch requests | `composite.md` | | Metadata and schema | `metadata.md` | | Error handling | `errors.md` | ## Core Rules 1. **Bearer token auth** - `Authorization: Bearer $SF_ACCESS_TOKEN` 2. **Instance URL required** - Each org has unique URL like `https://yourorg.my.salesforce.com` 3. **API version in path** - Use `/services/data/v59.0/` (or newer) 4. **SOQL for queries** - Salesforce Object Query Language for data retrieval 5. **Object API names** - Use API names: `Account`, `Contact__c` (custom objects end in `__c`) 6. **Pagination for large results** - Use `nextRecordsUrl` for results over 2000 7. **Rate limits** - Vary by edition, typically 100k+ calls/day ## Authentication **Required environment variables:** - `SF_ACCESS_TOKEN` - OAuth access token for API calls - `SF_INSTANCE_URL` - Your Salesforce instance (e.g., `https://yourorg.my.salesforce.com`) ```bash # All requests require these headers curl "$SF_INSTANCE_URL/services/data/v59.0/..." \ -H "Authorization: Bearer $SF_ACCESS_TOKEN" \ -H "Content-Type: application/json" ``` ## Common Traps - Wrong instance URL - 404 or DNS errors - Expired access token - 401 INVALID_SESSION_ID - Missing field permissions - Field appears null - SOQL syntax errors - MALFORMED_QUERY - Relationship name vs field name - Use `Account.Name` not `AccountId.Name` ## External Endpoints | Endpoint | Purpose | |----------|---------| | `https://*.my.salesforce.com/services/data/*` | REST API | | `https://*.my.salesforce.com/services/async/*` | Bulk API | | `https://login.salesforce.com/services/oauth2/*` | OAuth (production) | | `https://test.salesforce.com/services/oauth2/*` | OAuth (sandbox) | ## Security & Privacy **Environment variables used:** - `SF_ACCESS_TOKEN` - for API authentication - `SF_INSTANCE_URL` - for API endpoint routing **Sent to Salesforce:** Queries, record data via your instance URL **Stays local:** Access token (in environment variable only), ~/salesforce-api-integration/ preferences **Never:** Store tokens in files, log sensitive data, bypass field-level security ## Scope This skill ONLY: - Makes requests to Salesforce REST API endpoints - Stores preferences in `~/salesforce-api-integration/` - Provides curl and code examples This skill NEVER: - Accesses files outside `~/salesforce-api-integration/` - Makes requests to non-Salesforce endpoints - Stores access tokens in files ## Trust By using this skill, data is sent to Salesforce (salesforce.com). Only install if you trust Salesforce with your CRM data. ## Related Skills Install with `clawhub install <slug>` if user confirms: - `api` — REST API patterns - `crm` — CRM workflows - `accounting` — Financial operations ## Feedback - If useful: `clawhub star salesforce-api-integration` - Stay updated: `clawhub sync`

path: bulk.md
content: # Bulk API 2.0 - Salesforce API

For large datasets (thousands to millions of records).

## When to Use Bulk API

| Scenario | Use |
|----------|-----|
| < 200 records | Standard REST API |
| 200 - 10,000 records | Standard REST or Bulk |
| > 10,000 records | Bulk API 2.0 |

## Create Bulk Job

### Insert Job

```bash
curl -X POST "$SF_INSTANCE_URL/services/data/v59.0/jobs/ingest" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "operation": "insert",
    "object": "Account",
    "contentType": "CSV",
    "lineEnding": "LF"
  }'
```

Response:
```json
{
  "id": "750xx000000JOBID",
  "state": "Open",
  "operation": "insert",
  "object": "Account"
}
```

### Update Job

```bash
curl -X POST "$SF_INSTANCE_URL/services/data/v59.0/jobs/ingest" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "operation": "update",
    "object": "Account",
    "contentType": "CSV"
  }'
```

### Upsert Job

```bash
curl -X POST "$SF_INSTANCE_URL/services/data/v59.0/jobs/ingest" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "operation": "upsert",
    "object": "Account",
    "externalIdFieldName": "External_ID__c",
    "contentType": "CSV"
  }'
```

### Delete Job

```bash
curl -X POST "$SF_INSTANCE_URL/services/data/v59.0/jobs/ingest" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "operation": "delete",
    "object": "Account",
    "contentType": "CSV"
  }'
```

## Upload Data

After creating job, upload CSV:

```bash
curl -X PUT "$SF_INSTANCE_URL/services/data/v59.0/jobs/ingest/750xx000000JOBID/batches" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN" \
  -H "Content-Type: text/csv" \
  --data-binary @accounts.csv
```

CSV format for insert:
```csv
Name,Industry,Website
Acme Corp,Technology,https://acme.com
Beta Inc,Finance,https://beta.com
```

CSV format for update/delete (requires Id):
```csv
Id,Name,Industry
001xx000003ABC,Acme Updated,Healthcare
001xx000003DEF,Beta Updated,Technology
```

## Close Job (Start Processing)

```bash
curl -X PATCH "$SF_INSTANCE_URL/services/data/v59.0/jobs/ingest/750xx000000JOBID" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"state": "UploadComplete"}'
```

## Check Job Status

```bash
curl "$SF_INSTANCE_URL/services/data/v59.0/jobs/ingest/750xx000000JOBID" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN"
```

States:
- `Open` - Ready for data upload
- `UploadComplete` - Data uploaded, processing
- `InProgress` - Processing records
- `JobComplete` - All records processed
- `Failed` - Job failed
- `Aborted` - Job aborted

## Get Results

### Successful Records

```bash
curl "$SF_INSTANCE_URL/services/data/v59.0/jobs/ingest/750xx000000JOBID/successfulResults" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN"
```

### Failed Records

```bash
curl "$SF_INSTANCE_URL/services/data/v59.0/jobs/ingest/750xx000000JOBID/failedResults" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN"
```

### Unprocessed Records

```bash
curl "$SF_INSTANCE_URL/services/data/v59.0/jobs/ingest/750xx000000JOBID/unprocessedrecords" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN"
```

## Bulk Query

### Create Query Job

```bash
curl -X POST "$SF_INSTANCE_URL/services/data/v59.0/jobs/query" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "operation": "query",
    "query": "SELECT Id, Name, Industry FROM Account"
  }'
```

### Get Query Results

```bash
curl "$SF_INSTANCE_URL/services/data/v59.0/jobs/query/750xx000000JOBID/results" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN"
```

## Abort Job

```bash
curl -X PATCH "$SF_INSTANCE_URL/services/data/v59.0/jobs/ingest/750xx000000JOBID" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"state": "Aborted"}'
```

## Delete Job

```bash
curl -X DELETE "$SF_INSTANCE_URL/services/data/v59.0/jobs/ingest/750xx000000JOBID" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN"
```

## List Jobs

```bash
curl "$SF_INSTANCE_URL/services/data/v59.0/jobs/ingest" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN"
```

## Limits

- Max 150MB per file upload
- Max 100 million records per 24h
- Max 15,000 batches per 24h
- Query jobs: max 15 concurrent


path: composite.md
content: # Composite API - Salesforce API

Execute multiple operations in a single request.

## Composite Request

Up to 25 subrequests in one call:

```bash
curl -X POST "$SF_INSTANCE_URL/services/data/v59.0/composite" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "allOrNone": true,
    "compositeRequest": [
      {
        "method": "POST",
        "url": "/services/data/v59.0/sobjects/Account",
        "referenceId": "newAccount",
        "body": {"Name": "Acme Corp"}
      },
      {
        "method": "POST",
        "url": "/services/data/v59.0/sobjects/Contact",
        "referenceId": "newContact",
        "body": {
          "LastName": "Smith",
          "AccountId": "@{newAccount.id}"
        }
      }
    ]
  }'
```

- `allOrNone`: true = rollback all if any fails
- `referenceId`: Reference results in later requests with `@{refId.field}`

## Batch Request

Execute multiple independent requests:

```bash
curl -X POST "$SF_INSTANCE_URL/services/data/v59.0/composite/batch" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "batchRequests": [
      {
        "method": "GET",
        "url": "v59.0/sobjects/Account/001xxx"
      },
      {
        "method": "GET",
        "url": "v59.0/sobjects/Contact/003xxx"
      }
    ]
  }'
```

## SObject Tree (Create Related Records)

Create parent and children in one request:

```bash
curl -X POST "$SF_INSTANCE_URL/services/data/v59.0/composite/tree/Account" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "records": [
      {
        "attributes": {"type": "Account", "referenceId": "ref1"},
        "Name": "Parent Account",
        "Contacts": {
          "records": [
            {
              "attributes": {"type": "Contact", "referenceId": "ref2"},
              "LastName": "Smith"
            },
            {
              "attributes": {"type": "Contact", "referenceId": "ref3"},
              "LastName": "Jones"
            }
          ]
        }
      }
    ]
  }'
```

Response includes IDs for all created records.

## SObject Collections

### Create Multiple Records

```bash
curl -X POST "$SF_INSTANCE_URL/services/data/v59.0/composite/sobjects" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "allOrNone": false,
    "records": [
      {
        "attributes": {"type": "Account"},
        "Name": "Account 1"
      },
      {
        "attributes": {"type": "Account"},
        "Name": "Account 2"
      }
    ]
  }'
```

### Update Multiple Records

```bash
curl -X PATCH "$SF_INSTANCE_URL/services/data/v59.0/composite/sobjects" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "allOrNone": false,
    "records": [
      {
        "attributes": {"type": "Account"},
        "Id": "001xxx",
        "Industry": "Technology"
      },
      {
        "attributes": {"type": "Account"},
        "Id": "001yyy",
        "Industry": "Finance"
      }
    ]
  }'
```

### Delete Multiple Records

```bash
curl -X DELETE "$SF_INSTANCE_URL/services/data/v59.0/composite/sobjects?ids=001xxx,001yyy&allOrNone=false" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN"
```

### Get Multiple Records

```bash
curl -X POST "$SF_INSTANCE_URL/services/data/v59.0/composite/sobjects/Account" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "ids": ["001xxx", "001yyy"],
    "fields": ["Id", "Name", "Industry"]
  }'
```

## Composite Graph

Complex operations with dependencies:

```bash
curl -X POST "$SF_INSTANCE_URL/services/data/v59.0/composite/graph" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "graphs": [
      {
        "graphId": "graph1",
        "compositeRequest": [
          {
            "method": "POST",
            "url": "/services/data/v59.0/sobjects/Account",
            "referenceId": "newAccount",
            "body": {"Name": "Graph Account"}
          }
        ]
      }
    ]
  }'
```

## Limits

| Operation | Limit |
|-----------|-------|
| Composite subrequests | 25 |
| Batch requests | 25 |
| SObject Collections | 200 records |
| SObject Tree | 200 records total |
| Composite Graph | 500 nodes |


path: errors.md
content: # Error Handling - Salesforce API

## Error Response Format

```json
[
  {
    "errorCode": "INVALID_FIELD",
    "message": "No such column 'Industryy' on entity 'Account'",
    "fields": ["Industryy"]
  }
]
```

## Common Error Codes

### Authentication Errors

| Code | HTTP | Cause | Fix |
|------|------|-------|-----|
| `INVALID_SESSION_ID` | 401 | Token expired or invalid | Re-authenticate |
| `INVALID_AUTH_HEADER` | 401 | Malformed Authorization header | Check Bearer format |
| `INVALID_LOGIN` | 401 | Bad credentials | Verify username/password |

### Authorization Errors

| Code | HTTP | Cause | Fix |
|------|------|-------|-----|
| `INSUFFICIENT_ACCESS` | 403 | No permission | Check profile/permission set |
| `FIELD_INTEGRITY_EXCEPTION` | 400 | Field-level security | Grant field access |
| [REDACTED_SECRET] | 400 | Trigger/validation error | Check automation |

### Query Errors

| Code | HTTP | Cause | Fix |
|------|------|-------|-----|
| `MALFORMED_QUERY` | 400 | SOQL syntax error | Fix query syntax |
| `INVALID_FIELD` | 400 | Field doesn't exist | Check field API name |
| `INVALID_TYPE` | 400 | Object doesn't exist | Check object API name |
| `QUERY_TOO_COMPLICATED` | 400 | Query too complex | Simplify or split |

### Record Errors

| Code | HTTP | Cause | Fix |
|------|------|-------|-----|
| `NOT_FOUND` | 404 | Record doesn't exist | Verify record ID |
| `DUPLICATE_VALUE` | 400 | Unique field violation | Check for duplicates |
| `REQUIRED_FIELD_MISSING` | 400 | Required field empty | Provide required fields |
| [REDACTED_SECRET] | 400 | Validation rule failed | Check validation rules |
| `ENTITY_IS_DELETED` | 400 | Record was deleted | Record in recycle bin |

### Limit Errors

| Code | HTTP | Cause | Fix |
|------|------|-------|-----|
| `REQUEST_LIMIT_EXCEEDED` | 403 | API limit reached | Wait or reduce calls |
| [REDACTED_SECRET] | 400 | Too many subqueries | Split query |
| `QUERY_TIMEOUT` | 400 | Query took too long | Add filters/limits |

## HTTP Status Codes

| Status | Meaning |
|--------|---------|
| 200 | Success (GET, query) |
| 201 | Created (POST) |
| 204 | No Content (PATCH, DELETE success) |
| 400 | Bad Request (malformed query, missing field) |
| 401 | Unauthorized (auth failed) |
| 403 | Forbidden (no permission) |
| 404 | Not Found (bad ID or URL) |
| 405 | Method Not Allowed |
| 500 | Server Error (retry) |

## Handling Token Expiration

Access tokens expire. Detect and refresh:

```python
def sf_request(method, url, **kwargs):
    response = requests.request(method, url, **kwargs)
    
    if response.status_code == 401:
        error = response.json()
        if error[0].get('errorCode') == 'INVALID_SESSION_ID':
            # Refresh token and retry
            new_token = refresh_access_token()
            kwargs['headers']['Authorization'] = f'Bearer {new_token}'
            return requests.request(method, url, **kwargs)
    
    return response
```

## Retry Strategy

```python
import time

def sf_request_with_retry(method, url, **kwargs):
    max_retries = 3
    
    for attempt in range(max_retries):
        response = requests.request(method, url, **kwargs)
        
        if response.status_code == 503:  # Service unavailable
            time.sleep(2 ** attempt)
            continue
        
        if response.status_code == 403:
            error = response.json()
            if 'REQUEST_LIMIT_EXCEEDED' in str(error):
                time.sleep(60)  # Wait for limit reset
                continue
        
        return response
    
    raise Exception("Max retries exceeded")
```

## Validation Rule Errors

When validation rules fail:

```json
[
  {
    "errorCode": "FIELD_CUSTOM_VALIDATION_EXCEPTION",
    "message": "Email must be company domain",
    "fields": ["Email"]
  }
]
```

## Trigger Errors

When triggers fail:

```json
[
  {
    "errorCode": "CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY",
    "message": "AccountTrigger: execution of BeforeInsert...",
    "fields": []
  }
]
```

## Debugging Tips

1. **Check field API names** - Use describe to verify names
2. **Test in Workbench** - Validate SOQL before API calls
3. **Check permissions** - Profile, permission sets, field-level security
4. **Review automation** - Triggers, flows, validation rules
5. **Check limits** - API calls, storage, governor limits
6. **Use debug logs** - Enable in Setup → Debug Logs


path: memory-template.md
content: # Memory Template — Salesforce API

Create `~/salesforce-api-integration/memory.md` with this structure:

```markdown
# Salesforce Memory

## Status
status: ongoing
version: 1.0.0
last: YYYY-MM-DD
integration: pending | done | declined

## Context
<!-- What you know about their Salesforce org -->
<!-- Org type: production or sandbox -->
<!-- Main objects they work with (Account, Contact, custom objects) -->
<!-- Common operations they request -->

## Objects
<!-- Key objects they've mentioned or queried -->
<!-- Standard: Account, Contact, Opportunity, Lead, Case -->
<!-- Custom: ObjectName__c (note the __c suffix) -->

## Queries
<!-- SOQL patterns they use frequently -->
<!-- Save queries that work well for them -->

## Notes
<!-- Internal observations -->
<!-- Field names they use frequently -->
<!-- Any API version preferences -->

---
*Updated: YYYY-MM-DD*
```

## Status Values

| Value | Meaning | Behavior |
|-------|---------|----------|
| `ongoing` | Still learning | Ask questions when relevant |
| `complete` | Has enough context | Work normally |
| `paused` | User said "not now" | Don't ask, work with what you have |
| `never_ask` | User said stop | Never ask for more context |

## Key Principles

- **No config keys visible** — use natural language, not "default_limit: 100"
- **Learn from behavior** — if they always query Accounts first, remember that
- **Track objects loosely** — note what they use but don't maintain full schema
- Update `last` on each use


path: metadata.md
content: # Metadata and Schema - Salesforce API

## List All Objects

```bash
curl "$SF_INSTANCE_URL/services/data/v59.0/sobjects/" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN"
```

## Describe Object

Get full schema including fields, relationships, picklist values:

```bash
curl "$SF_INSTANCE_URL/services/data/v59.0/sobjects/Account/describe" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN"
```

Response includes:
- `fields[]` - All field definitions
- `childRelationships[]` - Related child objects
- `recordTypeInfos[]` - Available record types
- `urls{}` - API endpoints for this object

## Describe Field

Get specific field details:

```bash
# Via describe (filter response)
curl "$SF_INSTANCE_URL/services/data/v59.0/sobjects/Account/describe" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN" | jq '.fields[] | select(.name=="Industry")'
```

## Field Types

| Type | Example | Notes |
|------|---------|-------|
| id | `001xx000003ABC` | 15 or 18 char ID |
| string | `"Acme Corp"` | Text field |
| textarea | `"Long text..."` | Long text |
| boolean | `true` / `false` | Checkbox |
| int | `42` | Integer |
| double | `3.14159` | Decimal |
| currency | `1000.00` | Money amount |
| percent | `25.5` | Percentage |
| date | `"2024-12-31"` | Date only |
| datetime | `"2024-12-31T14:30:00.000Z"` | Date and time |
| time | `"14:30:00.000Z"` | Time only |
| email | `"[REDACTED_SECRET]"` | Email address |
| phone | `"+1-555-123-4567"` | Phone number |
| url | `"https://example.com"` | Website |
| picklist | `"Technology"` | Single select |
| multipicklist | `"A;B;C"` | Multi-select (semicolon separated) |
| reference | `"001xx000003ABC"` | Lookup/Master-Detail |

## Get Picklist Values

```bash
# From describe
curl "$SF_INSTANCE_URL/services/data/v59.0/sobjects/Account/describe" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN" | jq '.fields[] | select(.name=="Industry") | .picklistValues'
```

## Record Types

```bash
curl "$SF_INSTANCE_URL/services/data/v59.0/sobjects/Account/describe" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN" | jq '.recordTypeInfos'
```

## Page Layouts

```bash
curl "$SF_INSTANCE_URL/services/data/v59.0/sobjects/Account/describe/layouts" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN"
```

## Compact Layouts

```bash
curl "$SF_INSTANCE_URL/services/data/v59.0/sobjects/Account/describe/compactLayouts" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN"
```

## Global Describe

Summary of all objects:

```bash
curl "$SF_INSTANCE_URL/services/data/v59.0/sobjects/" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN"
```

## API Limits

```bash
curl "$SF_INSTANCE_URL/services/data/v59.0/limits/" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN"
```

Response includes:
- `DailyApiRequests`
- `DailyBulkApiRequests`
- `DailyStreamingApiEvents`
- `DataStorageMB`
- `FileStorageMB`

## API Versions

```bash
curl "$SF_INSTANCE_URL/services/data/"
```

## Resources

```bash
curl "$SF_INSTANCE_URL/services/data/v59.0/" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN"
```

## Tooling API

For metadata like Apex classes, triggers:

```bash
# List Apex classes
curl "$SF_INSTANCE_URL/services/data/v59.0/tooling/sobjects/ApexClass" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN"

# Query Apex classes
curl "$SF_INSTANCE_URL/services/data/v59.0/tooling/query/?q=SELECT+Id,Name,Status+FROM+ApexClass" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN"

# Describe tooling object
curl "$SF_INSTANCE_URL/services/data/v59.0/tooling/sobjects/ApexClass/describe" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN"
```

## Custom Metadata Types

```bash
# List custom metadata types
curl "$SF_INSTANCE_URL/services/data/v59.0/tooling/query/?q=SELECT+DeveloperName,Label+FROM+CustomObject+WHERE+DeveloperName+LIKE+'%__mdt'" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN"
```


path: objects.md
content: # Standard Objects - Salesforce API

## Account

Company or organization.

**Key Fields:**
| Field | Type | Description |
|-------|------|-------------|
| Id | ID | Unique identifier |
| Name | String | Account name (required) |
| Industry | Picklist | Industry type |
| Website | URL | Company website |
| Phone | Phone | Main phone |
| BillingCity | String | Billing address city |
| OwnerId | Reference | Record owner |
| ParentId | Reference | Parent account |

```bash
# Get account
curl "$SF_INSTANCE_URL/services/data/v59.0/sobjects/Account/001xxx" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN"

# Create account
curl -X POST "$SF_INSTANCE_URL/services/data/v59.0/sobjects/Account" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"Name": "Acme Corp", "Industry": "Technology"}'
```

## Contact

Individual person associated with an Account.

**Key Fields:**
| Field | Type | Description |
|-------|------|-------------|
| Id | ID | Unique identifier |
| FirstName | String | First name |
| LastName | String | Last name (required) |
| Email | Email | Email address |
| Phone | Phone | Phone number |
| AccountId | Reference | Parent account |
| Title | String | Job title |
| OwnerId | Reference | Record owner |

```bash
# Get contacts for account
curl "$SF_INSTANCE_URL/services/data/v59.0/query/?q=SELECT+Id,FirstName,LastName,Email+FROM+Contact+WHERE+AccountId='001xxx'" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN"
```

## Opportunity

Sales deal or potential revenue.

**Key Fields:**
| Field | Type | Description |
|-------|------|-------------|
| Id | ID | Unique identifier |
| Name | String | Opportunity name (required) |
| AccountId | Reference | Related account |
| Amount | Currency | Deal value |
| StageName | Picklist | Sales stage (required) |
| CloseDate | Date | Expected close (required) |
| Probability | Percent | Win probability |
| IsClosed | Boolean | Is closed (won or lost) |
| IsWon | Boolean | Is closed-won |

**Common Stages:**
- Prospecting
- Qualification
- Needs Analysis
- Proposal
- Negotiation
- Closed Won
- Closed Lost

```bash
# Open opportunities over $10k
curl "$SF_INSTANCE_URL/services/data/v59.0/query/?q=SELECT+Id,Name,Amount,StageName+FROM+Opportunity+WHERE+IsClosed=false+AND+Amount>10000" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN"
```

## Lead

Potential customer not yet converted.

**Key Fields:**
| Field | Type | Description |
|-------|------|-------------|
| Id | ID | Unique identifier |
| FirstName | String | First name |
| LastName | String | Last name (required) |
| Company | String | Company name (required) |
| Email | Email | Email address |
| Status | Picklist | Lead status |
| IsConverted | Boolean | Has been converted |
| ConvertedAccountId | Reference | Converted account |
| ConvertedContactId | Reference | Converted contact |

**Common Statuses:**
- Open - Not Contacted
- Working - Contacted
- Closed - Converted
- Closed - Not Converted

```bash
# Unconverted leads
curl "$SF_INSTANCE_URL/services/data/v59.0/query/?q=SELECT+Id,Name,Company,Email,Status+FROM+Lead+WHERE+IsConverted=false" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN"
```

## Case

Customer support issue.

**Key Fields:**
| Field | Type | Description |
|-------|------|-------------|
| Id | ID | Unique identifier |
| CaseNumber | String | Auto-generated number |
| Subject | String | Case subject |
| Description | TextArea | Case details |
| Status | Picklist | Case status |
| Priority | Picklist | Priority level |
| AccountId | Reference | Related account |
| ContactId | Reference | Related contact |
| IsClosed | Boolean | Is resolved |

**Common Statuses:**
- New
- Working
- Escalated
- Closed

```bash
# Open high-priority cases
curl "$SF_INSTANCE_URL/services/data/v59.0/query/?q=SELECT+Id,CaseNumber,Subject,Priority+FROM+Case+WHERE+IsClosed=false+AND+Priority='High'" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN"
```

## Task

Activity to be completed.

**Key Fields:**
| Field | Type | Description |
|-------|------|-------------|
| Id | ID | Unique identifier |
| Subject | String | Task subject |
| Status | Picklist | Task status |
| Priority | Picklist | Priority |
| ActivityDate | Date | Due date |
| WhoId | Reference | Contact/Lead |
| WhatId | Reference | Account/Opportunity |
| IsClosed | Boolean | Is completed |

## Event

Calendar event or meeting.

**Key Fields:**
| Field | Type | Description |
|-------|------|-------------|
| Id | ID | Unique identifier |
| Subject | String | Event subject |
| StartDateTime | DateTime | Start time |
| EndDateTime | DateTime | End time |
| WhoId | Reference | Contact/Lead |
| WhatId | Reference | Account/Opportunity |

## Custom Objects

Custom objects end in `__c`:

```bash
# List all objects (includes custom)
curl "$SF_INSTANCE_URL/services/data/v59.0/sobjects/" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN"

# Describe custom object
curl "$SF_INSTANCE_URL/services/data/v59.0/sobjects/MyObject__c/describe" \
  -H "Authorization: Bearer $SF_ACCESS_TOKEN"
```


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)