ERR_SF_OBJECT_PERM on Salesforce: Salesforce API returns INSUFFICIENT_ACCESS_OR_READONLY or ENTITY_IS_DELETED when attempting to read or write a record — the object exists but the integration user cannot access it. Root cause: Object-level permissions in Salesforce are controlled separately from field-level security. An integration user can have field-level read access but still be blocked at the object level if their Profile or Permission Set does not grant CRUD (Create, Read, Edit, Delete) access to the object. The ENTITY_IS_DELETED error occurs when an integration tries to access a soft-deleted record — Salesforce retains deleted records in the Recycle Bin for 15 days and they are queryable via SOQL with the ALL ROWS keyword, but standard API calls return this error for deleted records. Step 1: Identify the object and operation causing the error. The Salesforce API error response includes an errorCode and message that names the object and operation. INSUFFICIENT_ACCESS_OR_READONLY means the integration user lacks the required CRUD permission on the object. ENTITY_IS_DELETED means the record was soft-deleted. Note the Salesforce Object API name (e.g., Account, Opportunity, CustomObject__c) and the operation (read, create, update, delete) before proceeding. Step 2: Check object-level CRUD permissions on the integration user profile. In Salesforce Setup, go to Profiles → find the profile assigned to your integration user → Object Settings → find the object → verify the required CRUD checkboxes are enabled. For read operations, Read must be checked. For write operations, Edit must be checked. For creating new records, Create must be checked. If the profile is a standard profile (e.g., Standard User), you cannot edit its object permissions — create a custom profile or use a Permission Set instead. Step 3: Handle soft-deleted records with the ALL ROWS SOQL keyword. If your integration queries records that may have been deleted (e.g., syncing all contacts including recently deleted ones), use the ALL ROWS keyword in your SOQL query: SELECT Id, Name FROM Contact WHERE LastModifiedDate > LAST_N_DAYS:7 ALL ROWS. This returns both active and deleted records. Check the IsDeleted field on each returned record to identify deleted ones. For REST API calls to a specific record ID, there is no equivalent — you must catch the ENTITY_IS_DELETED error and handle it in your integration logic (e.g., mark the corresponding record as deleted in your external system). Step 4: Use the Salesforce Security Health Check to audit permissions. Salesforce provides a Security Health Check tool (Setup → Security → Health Check) that identifies overly permissive settings. For integration users, run a permission audit using the Profile Permission Report: Setup → Reports → create a report on Profiles and Permission Sets. Filter by your integration user's profile and export the object permissions to a spreadsheet. This gives you a complete view of what the integration user can and cannot access without clicking through each object individually. Step 5: Implement graceful error handling for permission failures. Integration code should handle Salesforce permission errors gracefully rather than crashing. Wrap Salesforce API calls in try/catch blocks and check for INSUFFICIENT_ACCESS_OR_READONLY and ENTITY_IS_DELETED error codes specifically. For permission errors, log the object, record ID, and operation, then skip the record and continue processing. For deleted record errors, update your external system to mark the record as deleted. Never retry permission errors automatically — they will not resolve without a configuration change. Step 6: Use a dedicated integration user with minimal required permissions. A common mistake is using an admin user's credentials for integrations — this works initially but creates security risks and makes it harder to audit what the integration is doing. Create a dedicated Salesforce user account for your integration (e.g., integration@yourcompany.com) with a custom profile that has only the minimum required object and field permissions. Assign this user a Permission Set for any additional access needed. This makes permission issues immediately visible (the integration user can't do X) rather than silently succeeding with excessive access.