{"id":14068222,"url":"https://github.com/StevenMMortimer/salesforcer","last_synced_at":"2025-07-30T03:32:38.802Z","repository":{"id":43347747,"uuid":"94126513","full_name":"StevenMMortimer/salesforcer","owner":"StevenMMortimer","description":"This R package connects the Salesforce APIs from R using tidy principles.","archived":false,"fork":false,"pushed_at":"2024-11-06T16:38:40.000Z","size":20761,"stargazers_count":84,"open_issues_count":10,"forks_count":19,"subscribers_count":15,"default_branch":"main","last_synced_at":"2025-05-16T22:06:42.254Z","etag":null,"topics":["api-wrappers","r","r-language","r-package","r-programming","salesforce","salesforce-apis"],"latest_commit_sha":null,"homepage":"https://stevenmmortimer.github.io/salesforcer/","language":"R","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/StevenMMortimer.png","metadata":{"files":{"readme":"README.Rmd","changelog":"NEWS.md","contributing":".github/CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":".github/SUPPORT.md","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"StevenMMortimer","ko_fi":"StevenMMortimer","tidelift":"cran/salesforcer"}},"created_at":"2017-06-12T18:14:00.000Z","updated_at":"2025-04-18T17:01:00.000Z","dependencies_parsed_at":"2025-01-01T01:12:07.684Z","dependency_job_id":"d6742cfc-0687-4e12-81cc-a5b0e07c1bdd","html_url":"https://github.com/StevenMMortimer/salesforcer","commit_stats":{"total_commits":459,"total_committers":6,"mean_commits":76.5,"dds":"0.47276688453159044","last_synced_commit":"6d8145799e845a01e81bb410b986b8779bc7a6a1"},"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/StevenMMortimer/salesforcer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StevenMMortimer%2Fsalesforcer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StevenMMortimer%2Fsalesforcer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StevenMMortimer%2Fsalesforcer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StevenMMortimer%2Fsalesforcer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/StevenMMortimer","download_url":"https://codeload.github.com/StevenMMortimer/salesforcer/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StevenMMortimer%2Fsalesforcer/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267803984,"owners_count":24146527,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-30T02:00:09.044Z","response_time":70,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api-wrappers","r","r-language","r-package","r-programming","salesforce","salesforce-apis"],"created_at":"2024-08-13T07:06:02.044Z","updated_at":"2025-07-30T03:32:37.258Z","avatar_url":"https://github.com/StevenMMortimer.png","language":"R","readme":"---\noutput: \n  github_document:\n    html_preview: false\n---\n\n```{r, echo = FALSE, message = FALSE}\nknitr::opts_chunk$set(\n  fig.align = 'center',\n  collapse = TRUE,\n  comment = \"#\u003e\",\n  fig.path = \"man/figures/README-\")\noptions(tibble.print_min = 5L, tibble.print_max = 5L)\n```\n\n# salesforcer\u003cimg src=\"man/figures/salesforcer.png\" width=\"120px\" align=\"right\" /\u003e\n\n\u003c!-- badges: start --\u003e\n[![R Build Status](https://github.com/stevenmmortimer/salesforcer/workflows/R-CMD-check/badge.svg)](https://github.com/stevenmmortimer/salesforcer/actions?workflow=R-CMD-check)\n[![CRAN Status](https://www.r-pkg.org/badges/version/salesforcer)](https://cran.r-project.org/package=salesforcer)\n[![Lifecycle: Stable](https://img.shields.io/badge/lifecycle-stable-brightgreen.svg)](https://lifecycle.r-lib.org/articles/stages.html#stable)\n[![Monthly Downloads](https://cranlogs.r-pkg.org/badges/last-month/salesforcer)](https://cran.r-project.org/package=salesforcer)\n[![Coverage Status](https://codecov.io/gh/stevenmmortimer/salesforcer/branch/main/graph/badge.svg)](https://app.codecov.io/gh/stevenmmortimer/salesforcer?branch=main)\n\u003c!-- badges: end --\u003e\n\n{salesforcer} is an R package that connects to Salesforce Platform APIs using \ntidy principles. The package implements actions from the REST, SOAP, Bulk 1.0, \nBulk 2.0, Reports and Dashboards, and Metadata APIs.   \n\nPackage features include: \n\n  * OAuth 2.0 (Single Sign On) and Basic (Username-Password) Authentication \n  methods (`sf_auth()`)\n  * CRUD (Create, Retrieve, Update, Delete) methods for records using the SOAP, \n  REST, and Bulk APIs \n  * Query records via the SOAP, REST, Bulk 1.0, and Bulk 2.0 APIs using `sf_query()`\n  * Manage and execute reports and dashboards with:  \n    * `sf_list_reports()`, `sf_create_report()`, `sf_run_report()`, and more\n  * Retrieve and modify metadata (Custom Objects, Fields, etc.) using the Metadata \n  API with:  \n    * `sf_describe_objects()`, `sf_create_metadata()`, `sf_update_metadata()`, and more\n  * Utilize backwards compatible functions for the {RForcecom} package, such as: \n    * `rforcecom.login()`, `rforcecom.getObjectDescription()`, `rforcecom.query()`, \n    `rforcecom.create()`\n  * Basic utility calls (`sf_user_info()`, `sf_server_timestamp()`, `sf_list_objects()`)\n  * Functions to assist with master data management (MDM) or data integrity of \n  records by finding duplicates (`sf_find_duplicates()`, `sf_find_duplicates_by_id()`), \n  merging records (`sf_merge()`), and converting leads (`sf_convert_lead()`)\n  * Recover (`sf_undelete()`) or delete from the Recycle Bin (`sf_empty_recycle_bin()`) \n  and list ids of records deleted (`sf_get_deleted()`) or updated (`sf_get_updated()`) \n  within a specific timeframe\n  * Passing API call control parameters such as, \"All or None\", \"Duplicate Rule\", \n  \"Assignment Rule\" execution and many more!  \n  \n## Table of Contents  \n  * [Installation](#installation)\n  * [Vignettes](#vignettes)\n  * [Usage](#usage)\n    * [Authenticate](#authenticate)\n    * [Create](#create)\n    * [Query](#query)\n    * [Update](#update)\n    * [Bulk Operations](#bulk-operations)\n    * [Using the Metadata API](#using-the-metadata-api)\n  * [Future](#future)\n  * [Credits](#credits)\n  * [More Information](#more-information)\n\n## Installation\n\n```{r, eval = FALSE}\n# install the current CRAN version\ninstall.packages(\"salesforcer\")\n\n# or get the development version on GitHub\n# install.packages(\"remotes\")\nremotes::install_github(\"StevenMMortimer/salesforcer\")\n```\n\nIf you encounter an issue while using this package, please file a minimal reproducible \nexample on [GitHub](https://github.com/stevenmmortimer/salesforcer/issues).\n\n## Vignettes\n\nThe README below outlines the basic package functionality. For more information \nplease feel free to browse the {salesforcer} website at https://stevenmmortimer.github.io/salesforcer/ which contains the following vignettes:\n\n  * [Getting Started](https://stevenmmortimer.github.io/salesforcer/articles/getting-started.html)\n  * [Supported Queries](https://stevenmmortimer.github.io/salesforcer/articles/supported-queries.html)\n  * [Working with Bulk APIs](https://stevenmmortimer.github.io/salesforcer/articles/working-with-bulk-apis.html)\n  * [Working with Reports](https://stevenmmortimer.github.io/salesforcer/articles/working-with-reports.html)\n  * [Working with Attachments](https://stevenmmortimer.github.io/salesforcer/articles/working-with-attachments.html)\n  * [Working with Metadata](https://stevenmmortimer.github.io/salesforcer/articles/working-with-metadata.html)\n  * [Passing Control Args](https://stevenmmortimer.github.io/salesforcer/articles/passing-control-args.html)\n  * [Transitioning from RForcecom](https://stevenmmortimer.github.io/salesforcer/articles/transitioning-from-RForcecom.html)\n\n## Usage\n\n### Authenticate\n\nFirst, load the {salesforcer} package and log in. There are two ways to \nauthenticate:  \n\n  1. OAuth 2.0\n  2. ~~Basic Username-Password~~\n  \n**NOTE**: Beginning February 1, 2022 authentication via a username and\npassword will not work in most Salesforce organizations. On that date Salesforce\nwill begin requiring customers to enable multi-factor authentication (MFA). The\nfunction `sf_auth()` will return the error message:\n\n```sh\nINVALID_LOGIN: Invalid username, password, security token; or user locked out.\n```\n\nIt has always been recommended to use OAuth 2.0 so that passwords do not have to\nbe shared or embedded within scripts. For more information on how OAuth 2.0 works\nwithin the {salesforcer} package, please read the \n[Getting Started](https://stevenmmortimer.github.io/salesforcer/articles/getting-started.html) \nvignette.\n\n```{r auth, include = FALSE}\nsuppressWarnings(suppressMessages(library(dplyr)))\nlibrary(salesforcer)\ntoken_path \u003c- Sys.getenv(\"SALESFORCER_TOKEN_PATH\")\nsf_auth(token = paste0(token_path, \"salesforcer_token.rds\"))\n```\n\n```{r, eval=FALSE}\nlibrary(dplyr, warn.conflicts = FALSE)\nlibrary(salesforcer)\n\n# Using OAuth 2.0 authentication\nsf_auth()\n```\n\nAfter logging in with `sf_auth()`, you can check your connectivity by looking at \nthe information returned about the current user. It should be information about you!\n\n```{r}\n# pull down information of person logged in\n# it's a simple easy call to get started \n# and confirm a connection to the APIs\nuser_info \u003c- sf_user_info()\nsprintf(\"Organization Id: %s\", user_info$organizationId)\nsprintf(\"User Id: %s\", user_info$userId)\n```\n\n### Create\n\nSalesforce has objects and those objects contain records. One default object is the \n\"Contact\" object. This example shows how to create two records in the Contact object.\n\n```{r create-records}\nn \u003c- 2\nnew_contacts \u003c- tibble(FirstName = rep(\"Test\", n),\n                       LastName = paste0(\"Contact-Create-\", 1:n))\ncreated_records \u003c- sf_create(new_contacts, object_name = \"Contact\")\ncreated_records\n```\n\n### Query\n\nSalesforce has proprietary form of SQL called SOQL (Salesforce Object Query \nLanguage). SOQL is a powerful tool that allows you to return the attributes of records \non almost any object in Salesforce including Accounts, Contacts, Tasks, Opportunities, \neven Attachments! Below is an example where we grab the data we just created \nincluding Account object information for which the Contact record is associated \nwith.\n\n```{r query-records}\nmy_soql \u003c- sprintf(\"SELECT Id, \n                           Account.Name, \n                           FirstName, \n                           LastName \n                    FROM Contact \n                    WHERE Id in ('%s')\", \n                   paste0(created_records$id , collapse = \"','\"))\nqueried_records \u003c- sf_query(my_soql)\nqueried_records\n```\n\n**NOTE**: In the example above, you'll notice that the `\"Account.Name\"` column\ndoes not appear in the results. This is because the SOAP and REST APIs only\nreturn an empty Account object for the record if there is no relationship to an\naccount (see \u003ca rel=\"noopener noreferrer\" target=\"_blank\"\nhref=\"https://github.com/stevenmmortimer/salesforcer/issues/78\"\u003e#78\u003c/a\u003e). There\nis no reliable way to extract and rebuild the empty columns based on the query\nstring. If there were Account information, an additional column titled\n`\"Account.Name\"` would appear in the results. Note, that the Bulk 1.0 and Bulk\n2.0 APIs will return `\"Account.Name\"` as a column of all `NA` values for this\nquery because they return results differently.\n\n### Update\n\nAfter creating records you can update them using `sf_update()`. Updating a record \nrequires you to pass the Salesforce `Id` of the record. Salesforce creates a unique \n18-character identifier on each record and uses that to know which record to \nattach the update information you provide. Simply include a field or column in your \nupdate dataset called \"Id\" and the information will be matched. Here is an example \nwhere we update each of the records we created earlier with a new first name \ncalled \"TestTest\".\n\n```{r update-records}\n# Update some of those records\nqueried_records \u003c- queried_records %\u003e%\n  mutate(FirstName = \"TestTest\")\n\nupdated_records \u003c- sf_update(queried_records, object_name = \"Contact\")\nupdated_records\n```\n\n```{r cleanup-records, include=FALSE}\ndeleted_records \u003c- sf_delete(updated_records$id)\ndeleted_records\n```\n\n### Bulk Operations\n\nFor really large operations (inserts, updates, upserts, deletes, and queries) Salesforce \nprovides the [Bulk 1.0](https://developer.salesforce.com/docs/atlas.en-us.api_asynch.meta/api_asynch/asynch_api_intro.htm) \nand [Bulk 2.0](https://developer.salesforce.com/docs/atlas.en-us.api_asynch.meta/api_asynch/introduction_bulk_api_2.htm) \nAPIs. In order to use the Bulk APIs in {salesforcer} you can just add `api_type = \"Bulk 1.0\"` \nor `api_type = \"Bulk 2.0\"` to your functions and the operation will be executed \nusing the Bulk APIs. It's that simple. \n\nThe benefits of using the Bulk API for larger datasets is that the operation will \nreduce the number of individual API calls (organization usually have a limit on \ntotal calls) and batching the requests in bulk is usually quicker than running thousands \nof individuals calls when your data is large. **Note:** the Bulk 2.0 API does **NOT** \nguarantee the order of the data submitted is preserved in the output. This means \nthat you must join on other data columns to match up the Ids that are returned \nin the output with the data you submitted. For this reason, Bulk 2.0 may not be \na good solution for creating, updating, or upserting records where you need to keep \ntrack of the created Ids. The Bulk 2.0 API would be fine for deleting records where \nyou only need to know which Ids were successfully deleted.\n\n```{r bulk-example}\n# create contacts using the Bulk API\nn \u003c- 2\nnew_contacts \u003c- tibble(FirstName = rep(\"Test\", n),\n                       LastName = paste0(\"Contact-Create-\", 1:n))\ncreated_records \u003c- sf_create(new_contacts, \"Contact\", api_type = \"Bulk 1.0\")\ncreated_records\n\n# query large recordsets using the Bulk API\nmy_soql \u003c- sprintf(\"SELECT Id,\n                           FirstName, \n                           LastName\n                    FROM Contact \n                    WHERE Id in ('%s')\", \n                   paste0(created_records$Id , collapse = \"','\"))\n\nqueried_records \u003c- sf_query(my_soql, \"Contact\", api_type = \"Bulk 1.0\")\nqueried_records\n\n# delete these records using the Bulk 2.0 API\ndeleted_records \u003c- sf_delete(queried_records$Id, \"Contact\", api_type = \"Bulk 2.0\")\ndeleted_records\n```\n\n### Using the Metadata API\n\nSalesforce is a very flexible platform in that it provides the \n[Metadata API](https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_intro.htm) \nfor users to create, read, update and delete their entire Salesforce environment from \nobjects to page layouts and more. This makes it very easy to programmatically setup \nand teardown the Salesforce environment. One common use case for the Metadata API \nis retrieving information about an object (fields, permissions, etc.). You can use \nthe `sf_read_metadata()` function to return a list of objects and their metadata. \nIn the example below we retrieve the metadata for the Account and Contact objects. \nNote that the `metadata_type` argument is \"CustomObject\". Standard Objects are an \nimplementation of CustomObjects, so they are returned using that metadata type.\n\n```{r metadata-read}\nread_obj_result \u003c- sf_read_metadata(metadata_type='CustomObject',\n                                    object_names=c('Account', 'Contact'))\nread_obj_result[[1]][c('fullName', 'label', 'sharingModel', 'enableHistory')]\nfirst_two_fields_idx \u003c- head(which(names(read_obj_result[[1]]) == 'fields'), 2)\n# show the first two returned fields of the Account object\nread_obj_result[[1]][first_two_fields_idx]\n```\n\nThe data is returned as a list because object definitions are highly nested representations. \nYou may notice that we are missing some really specific details, such as, the picklist \nvalues of a field with type \"Picklist\". You can get that information using \n`sf_describe_object_fields()`. Here is an example using `sf_describe_object_fields()` \nwhere we get a `tbl_df` with one row for each field on the Account object: \n\n```{r soap-describe-object-fields}\nacct_fields \u003c- sf_describe_object_fields('Account')\nacct_fields %\u003e% select(name, label, length, soapType, type)\n\n# show the picklist selection options for the Account Type field\nacct_fields %\u003e% \n  filter(label == \"Account Type\") %\u003e% \n  .$picklistValues\n```\n\n## Future\n\nFuture APIs to support (roughly in priority order):\n\n - [Connect (Chatter) REST API](https://developer.salesforce.com/docs/atlas.en-us.chatterapi.meta/chatterapi/intro_what_is_chatter_connect.htm)\n - [Analytics External Data API](https://developer.salesforce.com/docs/atlas.en-us.bi_dev_guide_ext_data.meta/bi_dev_guide_ext_data/bi_ext_data_overview.htm)\n - [Analytics REST API](https://developer.salesforce.com/docs/atlas.en-us.bi_dev_guide_rest.meta/bi_dev_guide_rest/bi_rest_overview.htm) \n - [Tooling API](https://developer.salesforce.com/docs/atlas.en-us.api_tooling.meta/api_tooling/intro_api_tooling.htm)\n - [Actions API](https://developer.salesforce.com/docs/atlas.en-us.api_action.meta/api_action/actions_intro.htm)\n - [Streaming API](https://developer.salesforce.com/docs/atlas.en-us.api_streaming.meta/api_streaming/intro_stream.htm)\n - [Place Order API](https://developer.salesforce.com/docs/atlas.en-us.api_placeorder.meta/api_placeorder/sforce_placeorder_rest_api_intro.htm)\n - [Industries API](https://developer.salesforce.com/docs/atlas.en-us.api_rest_industries.meta/api_rest_industries/intro.htm)\n - [Data.com API](https://developer.salesforce.com/docs/atlas.en-us.datadotcom_api_dev_guide.meta/datadotcom_api_dev_guide/datadotcom_api_dev_guide_intro.htm)  \n \n## Credits\n\nThis application uses other open source software components. The authentication \ncomponents are mostly verbatim copies of the routines established in the {googlesheets}\npackage (https://github.com/jennybc/googlesheets). Methods are inspired by the {RForcecom} \npackage (https://github.com/hiratake55/RForcecom). We acknowledge and are grateful \nto these developers for their contributions to open source.\n\n## More Information\n\nSalesforce provides client libraries and examples in many programming languages (Java, \nPython, Ruby, and PhP) but unfortunately R is not a supported language. However, \nmost all operations supported by the Salesforce APIs are available via this package. \nThis package makes requests best formatted to match what the APIs require as input. \nThis articulation is not perfect and continued progress will be made to add and improve \nfunctionality. For details on formatting, attributes, and methods please refer to \n[Salesforce's documentation](https://trailhead.salesforce.com/content/learn/modules/api_basics/api_basics_overview) as they are explained better there. More information \nis also available on the {salesforcer} pkgdown website at https://stevenmmortimer.github.io/salesforcer/.  \n\n[Get supported salesforcer with the Tidelift Subscription](https://tidelift.com?utm_source=cran-salesforcer\u0026utm_medium=referral\u0026utm_campaign=readme)\n\n---\nPlease note that this project is released with a [Contributor Code of Conduct](https://github.com/stevenmmortimer/salesforcer/blob/main/.github/CODE_OF_CONDUCT.md). \nBy participating in this project you agree to abide by its terms.  \n\n[Top](#salesforcer)\n","funding_links":["https://github.com/sponsors/StevenMMortimer","https://ko-fi.com/StevenMMortimer","https://tidelift.com/funding/github/cran/salesforcer","https://tidelift.com?utm_source=cran-salesforcer\u0026utm_medium=referral\u0026utm_campaign=readme"],"categories":["R"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FStevenMMortimer%2Fsalesforcer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FStevenMMortimer%2Fsalesforcer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FStevenMMortimer%2Fsalesforcer/lists"}