{
  "connector": "salesforce",
  "label": "Salesforce",
  "vendor_docs": "https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/",
  "summary": "A Salesforce org that answers the REST, SOAP, GraphQL, Bulk, Tooling and streaming APIs the official clients call, plus an MCP server, over one org of synthetic records.",
  "surfaces": [
    {
      "kind": "REST",
      "path": "/services/data/vXX.X",
      "notes": "sObjects, describe, SOQL `query`/`queryAll`, SOSL `search`, composite, limits, reports, UI API, Tooling API, Bulk API 1.0 and 2.0, Apex REST under `/services/apexrest`"
    },
    {
      "kind": "SOAP",
      "path": "/services/Soap",
      "notes": "partner `login`, partner API calls, Metadata API, Apex API"
    },
    {
      "kind": "GraphQL",
      "path": "/services/data/vXX.X/graphql",
      "notes": "UI API GraphQL queries"
    },
    {
      "kind": "Streaming",
      "path": "/cometd",
      "notes": "CometD/Bayeux for Change Data Capture and test-run channels"
    },
    {
      "kind": "gRPC",
      "path": "Pub/Sub API",
      "notes": "Change Data Capture over the Pub/Sub API"
    },
    {
      "kind": "OAuth",
      "path": "/services/oauth2/token",
      "notes": "OAuth token issue alongside the SOAP login"
    },
    {
      "kind": "MCP",
      "path": "/mcp",
      "notes": "tools modelled on Salesforce's hosted MCP servers"
    }
  ],
  "api_versions": [
    "REST v21.0 through v67.0"
  ],
  "supported": [
    {
      "item": "SOQL with relationship queries, aggregates and `nextRecordsUrl` pagination; SOSL search"
    },
    {
      "item": "sObject describe and global describe for the standard objects in the org"
    },
    {
      "item": "`simple_salesforce` and the official SDK paths listed in the README, over HTTP and TLS"
    },
    {
      "item": "Change Data Capture over CometD and the Pub/Sub gRPC API"
    },
    {
      "item": "Reports with fixed report definitions, and `ContentVersion` file downloads"
    },
    {
      "item": "MCP tools for querying, describing and reading records"
    },
    {
      "item": "The daily API request allowance, reported by `/limits` and in `Sforce-Limit-Info`"
    }
  ],
  "unsupported": [
    {
      "item": "Triggers, flows, validation-rule automation, workflow field updates, roll-up summaries and Apex execution"
    },
    {
      "item": "GraphQL introspection and subscriptions"
    },
    {
      "item": "Streaming channels other than Change Data Capture and test runs (no PushTopics, generic or platform-event channels)"
    },
    {
      "item": "Multi-currency orgs; the org is single-currency"
    },
    {
      "item": "Many standard objects and REST resources outside the documented object list"
    },
    {
      "item": "Most Metadata API writes and deploys"
    }
  ],
  "differences": [
    {
      "scenario": "Bulk API and async jobs",
      "difference": "Jobs complete synchronously; there is no queued or in-progress window.",
      "guidance": "Do not assert on intermediate job states. Poll as you would against Salesforce; the first poll already reports the final state."
    },
    {
      "scenario": "Change Data Capture replay",
      "difference": "Channel filters are not applied and the event log keeps a fixed 1000 events.",
      "guidance": "Do not assert on filtered channels or on replay beyond the last 1000 events."
    },
    {
      "scenario": "Apex REST endpoints",
      "difference": "Handlers are reimplemented in the replica; no Apex runs.",
      "guidance": "Assert on documented responses, not on side effects of custom Apex code."
    },
    {
      "scenario": "SOAP partner API",
      "difference": "The envelope maps onto the same record store as REST; there is no WSDL negotiation or SOAP header behaviour.",
      "guidance": "Do not assert on SOAP headers such as `AllOrNoneHeader` or `AssignmentRuleHeader`."
    },
    {
      "scenario": "Notes and recycle bin",
      "difference": "Notes have no version history, and deleted records do not age out of the recycle bin.",
      "guidance": "Do not assert that `queryAll` stops returning deleted rows after 15 days."
    },
    {
      "scenario": "Daily request allowance",
      "difference": "`Sforce-Limit-Info` is an exact, monotonic counter; a real org's consecutive values can arrive out of order. Exhaustion answers 403 `REQUEST_LIMIT_EXCEEDED` with no `Retry-After`, as an org does.",
      "guidance": "Assert that your client reads `Sforce-Limit-Info`, not on exact counts or on consecutive values increasing."
    },
    {
      "scenario": "File contents",
      "difference": "Files are small valid files of the declared format, not real documents.",
      "guidance": "Assert on format and metadata, not on document text."
    },
    {
      "scenario": "Security and sharing",
      "difference": "Profiles, permission sets, sharing and field-level security apply to the user a credential names, but not the whole platform security model; a token that names no user runs as the administrator.",
      "guidance": "Use a credential for a named user when you test access, and do not treat the replica as proof of a full org's security posture."
    }
  ],
  "faults_supported": [
    {
      "fault": "throttle",
      "behaviour": "403 `REQUEST_LIMIT_EXCEEDED` (\"TotalRequests Limit exceeded.\") with an exhausted `Sforce-Limit-Info` header"
    },
    {
      "fault": "fail_next",
      "behaviour": "The next N requests fail as `throttle` does, then clear"
    },
    {
      "fault": "quota_limit",
      "behaviour": "403 `REQUEST_LIMIT_EXCEEDED` (\"ApiBatchItems Limit exceeded.\") once the per-window quota is spent"
    },
    {
      "fault": "error_rate",
      "behaviour": "500 `UNKNOWN_EXCEPTION` with an ErrorId, for the given fraction of requests"
    },
    {
      "fault": "latency_ms",
      "behaviour": "Fixed added latency on every data-plane request"
    }
  ]
}
