{"id":15512341,"url":"https://github.com/forcedotcom/salesforce-deskcom-api","last_synced_at":"2025-10-12T09:31:18.532Z","repository":{"id":62355212,"uuid":"21370871","full_name":"forcedotcom/salesforce-deskcom-api","owner":"forcedotcom","description":"A lightweight, flexible library for desk.com APIv2, it supports basic auth and OAuth as authentication methods and is written to be auto discoverable.","archived":true,"fork":false,"pushed_at":"2022-02-12T08:36:04.000Z","size":634,"stargazers_count":21,"open_issues_count":2,"forks_count":6,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-09-07T07:28:34.818Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/forcedotcom.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null}},"created_at":"2014-07-01T00:59:51.000Z","updated_at":"2023-01-28T03:10:04.000Z","dependencies_parsed_at":"2022-10-31T10:47:07.812Z","dependency_job_id":null,"html_url":"https://github.com/forcedotcom/salesforce-deskcom-api","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/forcedotcom/salesforce-deskcom-api","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/forcedotcom%2Fsalesforce-deskcom-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/forcedotcom%2Fsalesforce-deskcom-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/forcedotcom%2Fsalesforce-deskcom-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/forcedotcom%2Fsalesforce-deskcom-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/forcedotcom","download_url":"https://codeload.github.com/forcedotcom/salesforce-deskcom-api/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/forcedotcom%2Fsalesforce-deskcom-api/sbom","scorecard":{"id":406707,"data":{"date":"2025-08-11","repo":{"name":"github.com/forcedotcom/salesforce-deskcom-api","commit":"67a083618090efa3ebcdfefc2e11ae1caa02f1de"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.8,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 2/27 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Maintained","score":0,"reason":"project is archived","details":["Warn: Repository is archived."],"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.txt:0","Info: FSF or OSI recognized license: BSD 3-Clause \"New\" or \"Revised\" License: LICENSE.txt:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Security-Policy","score":9,"reason":"security policy file detected","details":["Info: security policy file detected: github.com/forcedotcom/.github/SECURITY.md:1","Info: Found linked content: github.com/forcedotcom/.github/SECURITY.md:1","Warn: One or no descriptive hints of disclosure, vulnerability, and/or timelines in security policy","Info: Found text in security policy: github.com/forcedotcom/.github/SECURITY.md:1"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 6 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-18T21:28:30.254Z","repository_id":62355212,"created_at":"2025-08-18T21:28:30.254Z","updated_at":"2025-08-18T21:28:30.254Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279001054,"owners_count":26082991,"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-10-09T02:00:07.460Z","response_time":59,"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":[],"created_at":"2024-10-02T09:53:36.153Z","updated_at":"2025-10-12T09:31:17.668Z","avatar_url":"https://github.com/forcedotcom.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"# DeskApi a client for API v2\n## An official Desk API Client\n\nDeskApi takes the capabilities of the Desk.com API and wraps them up in a Ruby\nclient so that it's easy-as-pie to get working with your support site's API.\n\nDesk.com publishes a change log monthly, which you can keep up with at\n[dev.desk.com/API/changelog](http://dev.desk.com/API/changelog).\n\nWe do our best to keep DeskApi, but please don't hesitate to open an\n[issue](https://github.com/forcedotcom/salesforce-deskcom-api/issues) or send a\n[pull request](https://github.com/forcedotcom/salesforce-deskcom-api/pulls)\nif you find a bug or would like to new functionality added.\n\n## Getting Started\n### Installation\n\nEasy!\n\n```ruby\ngem install desk_api\n```\n\n### Configuration\n\n#### Authentication Mechanism\n\nThe Desk.com API allows you to access data using two authentication mechanisms:\n\n##### Basic Authentication\n\n- Username\n- Password\n- Subdomain or Endpoint\n\n##### OAuth 1.0a\n\n- Consumer Key\n- Consumer Secret\n- Access Token\n- Access Token Secret\n- Subdomain or Endpoint\n\n#### Trust is our #1 value\n\nWhatever option or method you choose, please make sure to **never put your\ncredentials in your source code**. This makes them available to everyone\nwith read access to your source, it makes your code harder to maintain and\nis just overall a bad idea. There are many alternatives, including configuration\nfiles, environmental variables, ...\n\n#### First Environmental Variables\n\n`DeskApi` is automatically configured if you choose to use environmental\nvariables. There are 8 possible variables but you don't have to set all of them.\nBased on the authentication mechanism you prefer you'll only have to specify:\n\n```bash\nexport DESK_USERNAME=thomas@example.com\nexport DESK_PASSWORD=somepassword\nexport DESK_CONSUMER_KEY=CONSUMER_KEY\nexport DESK_CONSUMER_SECRET=CONSUMER_SECRET\nexport DESK_TOKEN=TOKEN\nexport DESK_TOKEN_SECRET=TOKEN_SECRET\nexport DESK_SUBDOMAIN=devel\nexport DESK_ENDPOINT=https://devel.desk.com\n```\n\n#### Second Configuration Option\n\nConfigure `DeskApi` itself to send/receive requests by calling the `configure`\nmethod to set up your authentication credentials:\n\n```ruby\nDeskApi.configure do |config|\n  # basic authentication\n  config.username = 'thomas@example.com'\n  config.password = 'somepassword'\n\n  # oauth configuration\n  config.token           = 'TOKEN'\n  config.token_secret    = 'TOKEN_SECRET'\n  config.consumer_key    = 'CONSUMER_KEY'\n  config.consumer_secret = 'CONSUMER_SECRET'\n\n  config.endpoint = 'https://devel.desk.com'\nend\n\nDeskApi.get '/api/v2/topics'\nDeskApi.post '/api/v2/topics', name: 'My new Topic', allow_questions: true\nDeskApi.patch '/api/v2/topics/1', name: 'Changed the Topic Name'\nDeskApi.delete '/api/v2/topics/1'\n```\n\n#### Third Configuration Option\n\nInitialize a new `DeskApi::Client` to send/receive requests\n\nThis example shows you how to initialize a new client and the four main request\nmethods supported by the Desk.com API (`GET`, `POST`, `PATCH` and `DELETE`).\n\n```ruby\n# basic authentication\nclient = DeskApi::Client.new username: 'thomas@example.com', password: 'somepassword', endpoint: 'https://devel.desk.com'\n# oauth configuration\nclient = DeskApi::Client.new({\n  token: 'TOKEN',\n  token_secret: 'TOKEN_SECRET',\n  consumer_key: 'CONSUMER_KEY',\n  consumer_secret: 'CONSUMER_SECRET',\n  endpoint: 'https://devel.desk.com'\n})\n\nresponse = client.get '/api/v2/topics'\nresponse = client.post '/api/v2/topics', name: 'My new Topic', allow_questions: true\nresponse = client.patch '/api/v2/topics/1', name: 'Changed the Topic Name'\nresponse = client.delete '/api/v2/topics/1'\n```\n\n## Resources\n\nResources are automatically discovered by the DeskApi. When requesting a\nresource from DeskAPI, the client sends the request and returns a\n`DeskApi::Resource`. If the client receives an error back from the API a\n`DeskApi::Error` is raised.\n\n### Create Read Update Delete\n\nOne of the most important features; we support creating, updating and deleting\nresources but not all resources can be deleted or updated or created, if that's\nthe case for the resource you're trying to update, it'll throw a\n`DeskApi::Error::MethodNotAllowed` error. For ease of use and because we wanted\nto build as less business logic into the wrapper as possible, all of the methods\nare defined on each `DeskApi::Resource` and will be sent to desk.com. However\nthe API might respond with an error if you do things that aren't supported.\n\n```ruby\n# let's create an article\nnew_article = DeskApi.articles.create({\n                subject: 'Some Subject',\n                body: 'Some Body',\n                _links: {\n                  topic: DeskApi.topics.first.get_self\n                }\n              })\n\n# as you can see here a `Resource' always includes a method `.get_self'\n# which will return the link object to build relationships\n\n# updating the article\nupdated_article = new_article.update subject: 'Updated Subject'\n\n# deleting the article\nif updated_article.delete == true\n  # article has been deleted\nend\n\n# ATTENTION: Cases can not be deleted!\nbegin\n  DeskApi.cases.first.delete\nrescue DeskApi::Error::MethodNotAllowed =\u003e e\n  # too bad\nend\n```\n\n### Getters \u0026 Setters\n\nAs you have seen in prior examples for each field on the resource we create a\ngetter and setter.\n\n```ruby\ncustomer = DeskApi.customers.find(1)\n\nputs customer.first_name\nputs customer.last_name\nputs customer.title\n\n# for updates you can either use the setter or a hash\n\ncustomer.first_name = 'John'\ncustomer.last_name = 'Doe'\n\nupdated_customer = customer.update title: 'Master of the Universe'\n\n# users are not updatable\nbegin\n  user = DeskApi.users.first\n  user.update name: 'Not updatable'\nrescue DeskApi::Error::MethodNotAllowed\n  # too bad\nend\n```\n\n### Find\n\nThe method `by_url` can be called on the client, for backwards compatibility we\nhaven't yet removed it from the `DeskApi::Resource` but it will be removed once\nwe release v1 of this client. `by_url` will return a lazy loaded instance of the\nresource.\n\n```ruby\nfirst_reply = DeskApi.by_url '/api/v2/cases/1/replies/1'\n```\n\nSince the update to v0.5 of the API wrapper the `find` method can now be called\non all `DeskApi::Resource` instances. _Gotcha:_ It will rebuild the base path\nbased on the resource/collection it is called on. So if you call it on the cases\ncollection `DeskApi.cases.find 1` the path will look like this:\n`/api/v2/cases/:id`.\n\n| Method                                                      | Path                        |\n| ----------------------------------------------------------- | --------------------------- |\n| `DeskApi.cases.find(1)`                                     | `/api/v2/cases/1`           |\n| `DeskApi.cases.entries.find(1)`                             | `/api/v2/cases/1`           |\n| `DeskApi.cases.search(subject: 'Test').find(1)`             | `/api/v2/cases/1`           |\n| `DeskApi.cases.search(subject: 'Test').entries.find(1)`     | `/api/v2/cases/1`           |\n| `DeskApi.cases.entries.first.replies.find(1)`               | `/api/v2/cases/1/replies/1` |\n| `DeskApi.cases.entries.first.replies.entries.first.find(1)` | `/api/v2/cases/1/replies/1` |\n\n### Pagination\n\nAs mentioned above you can also navigate between resources and pages of\ncollections.\n\n```ruby\ncases = DeskApi.cases\ncases.entries.each do |my_case|\n  # do something with the case\nend\n\n# now move on to the next page\nnext_page = cases.next\nnext_page.entries.each do |my_case|\n  # do something with the case\nend\n\n# go back to the previous page\nprevious_page = next_page.previous\n\n# or go to the last page\nlast_page = previous_page.last\n\n# or go to the first page\nfirst_page = last_page.first\n```\n\n### `all` and `each_page`\n\nAs a recent addition we made it even easier to navigate through all the pages.\n\n```ruby\nDeskApi.cases.all do |current_case, current_page_number|\n  # do something with the case\nend\n\nDeskApi.cases.each_page do |current_page, current_page_number|\n  # do something with the current page\nend\n```\n\nBoth methods use the max `per_page` for the API endpoint for that particular\nresource.\n\n### List params\n\nSome lists allow for additional params like [cases](http://dev.desk.com/API/cases/#list).\nThis allows you to filter the cases endpoint by using a `company_id`,\n`customer_id` or `filter_id` list param.\n\n```ruby\n# fetch cases for the company with id 1\ncompanys_cases = DeskApi.cases(company_id: 1)\n\n# fetch cases for the customer with id 1\ncustomers_cases = DeskApi.cases(customer_id: 1)\n\n# fetch cases for the filter with id 1\nfilters_cases = DeskApi.cases(filter_id: 1)\n```\n\n### Sorting\n\nThere is a maximum `page` limit on some Desk.com API endpoints. As of right now\n(May 2014) the limit is 500 for all endpoints that are limited, but please\nconsult the #list documentation for each resource:\n\n- [Case list](http://dev.desk.com/API/cases#list)\n- [Company Case list](http://dev.desk.com/API/companies#cases-list)\n- [Customer Case list](http://dev.desk.com/API/customers#cases-list)\n- [Filter Case list](http://dev.desk.com/API/filters#list-cases)\n- [Case search](http://dev.desk.com/API/cases#search)\n- [Customer search](http://dev.desk.com/API/customers#search)\n- [Company search](http://dev.desk.com/API/companies#search)\n- [Article search](http://dev.desk.com/API/articles#search)\n\n\nTo work around page limits, you can specify `sort_field` and `sort_direction`\n\n```ruby\n# fetch cases sorted by updated_at direction desc\nsorted_cases = DeskApi.cases(sort_field: :updated_at, sort_direction: :desc)\n```\n\n### Links\n\nOnce a `DeskApi::Resource` has loaded, its\n[linked resources](http://dev.desk.com/API/using-the-api/#relationships)\ncan be retrieved by calling the linked resource as a method of the\n`DeskApi::Resource`, e.g.,\n\n```ruby\n# Get a ticket\nticket = DeskApi.cases.entries.first\n\n# Get the ticket's assigned_user from the ticket\nassigned_user = ticket.assigned_user\n\n# Get the customer from the ticket\ncustomer = ticket.customer\n\n# Get the customer's company from the customer.\ncompany = customer.company\n\n# Getting the name of the user who sent the first outbound reply on a ticket\nuser_name = DeskApi.\n              cases.\n              entries.\n              select { |my_case| my_case.type == 'email' }.\n              first.\n              replies.\n              entries.select { |reply| reply.direction == 'out' }.\n              first.\n              sent_by.\n              name\n```\n\n### Lazy loading\n\nResources are lazy loaded. This means that requests are only sent when you\nrequest data from the resource. This helps a lot with flying under the Desk.com\nAPI [rate limit](http://dev.desk.com/API/using-the-api/#rate-limits). E.g.,\n\n```ruby\nDeskApi.cases.page(10).per_page(50).entries.each do |my_case|\n  # in this method chain, no HTTP request is fired until `.entries'\nend\n\n# however if you request the current page number and the resource is not loaded\n# it'll send a request\nDeskApi.cases.page == 1\n```\n\n### Embedding\n\nSome endpoints support [embedding](http://dev.desk.com/API/using-the-api/#embedding)\nrelated resources. E.g., when getting a list of cases from `/api/v2/cases` you\ncan embed the customer on each case by adding `embed=` to the query\nstring: `/api/v2/cases?embed=customer`\n\nThe client supports this: `tickets_and_customers = DeskApi.cases.embed(:customer)`\n\nTaking advantage of `.embed` is a great way to be conscious of the rate limit\nand minimize necessary HTTP requests.\n\n\n**Not using embedded resources**\n```ruby\ntickets = DeskApi.cases.per_page(100).entries # HTTP request\ntickets.each do |ticket|\n  puts ticket.customer.first_name # HTTP request (100 iterations)\nend\n\n# Total Requests: 101\n```\n\n**Using embedded resources**\n```ruby\ntickets = DeskApi.cases.per_page(100).embed(:customer).entries # HTTP request\ntickets.each do |ticket|\n  puts ticket.customer.first_name # No HTTP Request, customer is embedded\nend\n\n# Total Requests: 1\n```\n\n**Using embedded resources in `find`**\n```ruby\nmy_case = DeskApi.cases.find(1, embed: :customer)\n# OR\nmy_case = DeskApi.cases.find(1, embed: [:customer, :assigned_user, :assigned_group, :message])\n\ncustomer = my_case.customer\nassigned_user = my_case.assigned_user\nassigned_group = my_case.assigned_group\n```\n\n### Downloading Attachments\n\nAttachment resources can be downloaded directly, a `Tempfile` is returned that can either be read or moved to the final location.\n\n```ruby\nticket     = DeskApi.cases.find(123)\nattachment = ticket.attachments.entries.first\nfile       = attachment.download\n\n# Read the file\ncontent    = file.read\n\n# Save the file\nFile.open('/path/to/my/storage', 'w') do |f|\n  f.write(content)\nend\n```\n\n### API Errors\n\nSometimes the API is going to return errors, eg. Validation Error. In these\ncases we wrap the API error into a `DeskApi::Error`. Here are the common errors:\n\n```ruby\nDeskApi::Error::BadRequest             #=\u003e 400 Status\nDeskApi::Error::Unauthorized           #=\u003e 401 Status\nDeskApi::Error::Forbidden              #=\u003e 403 Status\nDeskApi::Error::NotFound               #=\u003e 404 Status\nDeskApi::Error::MethodNotAllowed       #=\u003e 405 Status\nDeskApi::Error::NotAcceptable          #=\u003e 406 Status\nDeskApi::Error::Conflict               #=\u003e 409 Status\nDeskApi::Error::UnsupportedMediaType   #=\u003e 415 Status\nDeskApi::Error::UnprocessableEntity    #=\u003e 422 Status\nDeskApi::Error::TooManyRequests        #=\u003e 429 Status\n```\n\nPlease also have a look at all\n[desk.com API errors](http://dev.desk.com/API/using-the-api/#status-codes) and\ntheir respective meanings.\n\n## License\n\nCopyright (c) 2013-2018, Salesforce.com, Inc.\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification,\nare permitted provided that the following conditions are met:\n\n  * Redistributions of source code must retain the above copyright notice, this\n    list of conditions and the following disclaimer.\n\n  * Redistributions in binary form must reproduce the above copyright notice,\n    this list of conditions and the following disclaimer in the documentation\n    and/or other materials provided with the distribution.\n\n  * Neither the name of Salesforce.com nor the names of its contributors may be\n    used to endorse or promote products derived from this software without\n    specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\nANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fforcedotcom%2Fsalesforce-deskcom-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fforcedotcom%2Fsalesforce-deskcom-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fforcedotcom%2Fsalesforce-deskcom-api/lists"}