{
  "schema": "aaes/capability-pack/1",
  "provider": "salesforce",
  "version": "1.1.0",
  "description": "Salesforce CRM records: read accounts, create leads, update opportunities and delete accounts in one org.",
  "notes": "The endpoint host 'your-instance.my.salesforce.com.invalid' is a placeholder under the reserved .invalid name, so an unsubstituted pack resolves nowhere and fails closed: replace it with the org's own My Domain host before enabling, in the endpoints, the resource locator and the egress allowlist. OPPORTUNITY_ID and ACCOUNT_ID in the endpoint paths are placeholders too: Salesforce's sObject Rows resource targets one record as /sobjects/{Object}/{Id}, so update and delete must name the record they are bound to. Account retrieval goes through the query resource with one fixed SOQL statement pinned in the registered endpoint's q parameter: the capability reads Account records only, and a caller cannot supply its own SOQL, because the HTTP connector dials the registered endpoint and ignores the caller's query string. Pack version 1.1.0 replaced the 1.0.0 registration of salesforce.account.read, which registered the bare query resource and was described as accepting any SOQL statement. The lead, opportunity and delete capabilities also carry 1.1.0, because the .invalid placeholder host changed their registered endpoints in the same release; a capability's version is the pack version in which its registration last changed.\n\nReplace the ids before enabling. As of Spring '26 Salesforce restricts new connected-app creation and recommends external client apps for new integrations; the org's admin creates the app, and this pack does not. The pack pins API version v62.0 deliberately: Salesforce keeps serving pinned versions, and a version bump is a reviewed migration, not a silent edit. Credential custody was reviewed on 2026-09-21 against this pack's custody model (a long-lived OAuth access token held in the customer's vault and refreshed client-side), with the following findings.\n\nIssuance authority and delegation: tokens are minted by the org's authorizing server for an external client app or connected app that the org's own admin creates (as of Spring '26 new connected apps can't be created and external client apps are recommended), and each OAuth flow grants the app restricted, delegated access on the user's behalf. Scope: scopes are fixed in the app's OAuth settings at creation; the retrieved pages do not enumerate the scope catalog, so exact scope names are confirmed in the org's app configuration rather than assumed.\n\nAudience: the retrieved docs establish that the org's resource server validates the token for that org's REST API, but document no audience claim, so audience binding beyond org-scoped validation is not established. TTL: the app's session timeout value determines when an access token is no longer valid (the value is org-configured, not a documented constant); refresh-token lifetime is an admin policy, and Salesforce documents an idle-TTL control that invalidates refresh tokens idle for 30 days and requires Refresh Token Rotation for partner apps (compliance due May 11, 2026).\n\nRefresh ownership: the client performs the refresh-token flow, and under rotation must capture and securely store the new refresh token each time — refresh ownership sits with an external client-side process; the executor injects the vault-held header verbatim and performs no token lifecycle itself.\n\nRevocation: the endpoint https://{MyDomainName}.my.salesforce.com/services/oauth2/revoke invalidates an access token, or a refresh token together with all associated access tokens. Executor access and replay/exposure: the executor reads only the vault reference and injects the stored header value; bearer tokens are replayable while valid, and Salesforce's rotation control invalidates a stolen refresh token on next use while an IP-allowlist control can restrict where refresh requests originate.\n\nAlternate access paths: the org's UI, other OAuth flows and other apps retain access outside this pack, and revoking this pack's grant closes only this path. identity_federation (a per-decision token where the org supports it) remains one candidate stronger model, not an automatic pass. Credential custody review is complete. Bind the remaining owner placeholders before deployment.",
  "credential": {
    "secret_ref_id": "secret-ref-salesforce-oauth",
    "custody": "customer_vault",
    "inject": "header",
    "inject_name": "Authorization",
    "note": "A Salesforce access token (minted by an external client app or a connected app) held in the client's own secret manager. The stored material must be the complete Authorization header value (scheme included). A per-decision token obtained by identity federation is the stronger option where the org supports it; this pack registers the client-vault model as the default."
  },
  "capabilities": [
    {
      "capability_id": "salesforce.account.read",
      "version": "1.1.0",
      "kind": "tool",
      "risk_tier": "R0",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "pii",
      "egress_allowlist": [
        "your-instance.my.salesforce.com.invalid"
      ],
      "endpoint": "https://your-instance.my.salesforce.com.invalid/services/data/v62.0/query?q=SELECT+Id,Name,Type,Industry,BillingCity,BillingCountry,Phone,Website,OwnerId,LastModifiedDate+FROM+Account+ORDER+BY+LastModifiedDate+DESC+LIMIT+200",
      "method": "GET",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-salesforce-oauth",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "read"
      ],
      "resource_id": "salesforce-org",
      "description": "Read the 200 most recently modified Account records through the SOQL query resource, with a fixed query pinned in the registered endpoint: SELECT Id, Name, Type, Industry, BillingCity, BillingCountry, Phone, Website, OwnerId, LastModifiedDate FROM Account ORDER BY LastModifiedDate DESC LIMIT 200. The HTTP connector dials the registered endpoint and ignores a query string the caller supplies, so a caller cannot substitute another SOQL statement, object or field list through this capability. LIMIT 200 keeps the result inside one query batch, so no nextRecordsUrl follow-up is needed (that locator is a different path, which this registration would refuse). A deployment that needs another selection registers its own capability version with its own fixed query. Account data identifies individuals and is classified as pii (personal data).",
      "requires_review": true
    },
    {
      "capability_id": "salesforce.lead.create",
      "version": "1.1.0",
      "kind": "tool",
      "risk_tier": "R2",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "pii",
      "egress_allowlist": [
        "your-instance.my.salesforce.com.invalid"
      ],
      "endpoint": "https://your-instance.my.salesforce.com.invalid/services/data/v62.0/sobjects/Lead",
      "method": "POST",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-salesforce-oauth",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "create"
      ],
      "resource_id": "salesforce-org",
      "description": "Create a Lead record.",
      "requires_review": true
    },
    {
      "capability_id": "salesforce.opportunity.update",
      "version": "1.1.0",
      "kind": "tool",
      "risk_tier": "R2",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "financial",
      "egress_allowlist": [
        "your-instance.my.salesforce.com.invalid"
      ],
      "endpoint": "https://your-instance.my.salesforce.com.invalid/services/data/v62.0/sobjects/Opportunity/OPPORTUNITY_ID",
      "method": "PATCH",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-salesforce-oauth",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "update"
      ],
      "resource_id": "salesforce-org",
      "description": "Update one Opportunity record, including amount and stage; OPPORTUNITY_ID in the path is a placeholder for the record this capability is bound to.",
      "requires_review": true
    },
    {
      "capability_id": "salesforce.account.delete",
      "version": "1.1.0",
      "kind": "tool",
      "risk_tier": "R3",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "pii",
      "egress_allowlist": [
        "your-instance.my.salesforce.com.invalid"
      ],
      "endpoint": "https://your-instance.my.salesforce.com.invalid/services/data/v62.0/sobjects/Account/ACCOUNT_ID",
      "method": "DELETE",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-salesforce-oauth",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "delete"
      ],
      "resource_id": "salesforce-org",
      "description": "Delete one Account record; ACCOUNT_ID in the path is a placeholder for the record this capability is bound to. Delete is one of the designated actions requiring human approval, so it carries the minimum R3 tier; policy can raise it further.",
      "requires_review": true
    }
  ],
  "resources": [
    {
      "resource_id": "salesforce-org",
      "kind": "api",
      "locator": "https://your-instance.my.salesforce.com.invalid",
      "data_class": "financial",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN"
    }
  ],
  "review": {
    "state": "library",
    "date": "2026-10-22",
    "sources": [
      "https://developer.salesforce.com/docs/platform/api-rest/guide/intro-oauth-and-connected-apps.html (retrieved 2026-09-20)",
      "https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/ (retrieved 2026-09-20)",
      "https://developer.salesforce.com/docs/platform/api-rest/guide/resources-list.html (retrieved 2026-09-21)",
      "https://developer.salesforce.com/docs/platform/mobile-sdk/guide/oauth-refresh-token-flow.html (retrieved 2026-09-21)",
      "https://developer.salesforce.com/docs/platform/mobile-sdk/guide/oauth-revoking-tokens.html (retrieved 2026-09-21)",
      "https://developer.salesforce.com/docs/atlas.en-us.packagingGuide.meta/packagingGuide/secure_code_ac_eca.htm (retrieved 2026-09-21)"
    ],
    "reviewer_id": "owner-delegated"
  }
}
