chub-dev 0.1.0 → 0.1.2-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/README.md +55 -0
  2. package/bin/chub-mcp +2 -0
  3. package/dist/airtable/docs/database/javascript/DOC.md +1437 -0
  4. package/dist/airtable/docs/database/python/DOC.md +1735 -0
  5. package/dist/amplitude/docs/analytics/javascript/DOC.md +1282 -0
  6. package/dist/amplitude/docs/analytics/python/DOC.md +1199 -0
  7. package/dist/anthropic/docs/claude-api/javascript/DOC.md +503 -0
  8. package/dist/anthropic/docs/claude-api/python/DOC.md +389 -0
  9. package/dist/asana/docs/tasks/DOC.md +1396 -0
  10. package/dist/assemblyai/docs/transcription/DOC.md +1043 -0
  11. package/dist/atlassian/docs/confluence/javascript/DOC.md +1347 -0
  12. package/dist/atlassian/docs/confluence/python/DOC.md +1604 -0
  13. package/dist/auth0/docs/identity/javascript/DOC.md +968 -0
  14. package/dist/auth0/docs/identity/python/DOC.md +1199 -0
  15. package/dist/aws/docs/s3/javascript/DOC.md +1773 -0
  16. package/dist/aws/docs/s3/python/DOC.md +1807 -0
  17. package/dist/binance/docs/trading/javascript/DOC.md +1315 -0
  18. package/dist/binance/docs/trading/python/DOC.md +1454 -0
  19. package/dist/braintree/docs/gateway/javascript/DOC.md +1278 -0
  20. package/dist/braintree/docs/gateway/python/DOC.md +1179 -0
  21. package/dist/chromadb/docs/embeddings-db/javascript/DOC.md +1263 -0
  22. package/dist/chromadb/docs/embeddings-db/python/DOC.md +1707 -0
  23. package/dist/clerk/docs/auth/javascript/DOC.md +1220 -0
  24. package/dist/clerk/docs/auth/python/DOC.md +274 -0
  25. package/dist/cloudflare/docs/workers/javascript/DOC.md +918 -0
  26. package/dist/cloudflare/docs/workers/python/DOC.md +994 -0
  27. package/dist/cockroachdb/docs/distributed-db/DOC.md +1500 -0
  28. package/dist/cohere/docs/llm/DOC.md +1335 -0
  29. package/dist/datadog/docs/monitoring/javascript/DOC.md +1740 -0
  30. package/dist/datadog/docs/monitoring/python/DOC.md +1815 -0
  31. package/dist/deepgram/docs/speech/javascript/DOC.md +885 -0
  32. package/dist/deepgram/docs/speech/python/DOC.md +685 -0
  33. package/dist/deepl/docs/translation/javascript/DOC.md +887 -0
  34. package/dist/deepl/docs/translation/python/DOC.md +944 -0
  35. package/dist/deepseek/docs/llm/DOC.md +1220 -0
  36. package/dist/directus/docs/headless-cms/javascript/DOC.md +1128 -0
  37. package/dist/directus/docs/headless-cms/python/DOC.md +1276 -0
  38. package/dist/discord/docs/bot/javascript/DOC.md +1090 -0
  39. package/dist/discord/docs/bot/python/DOC.md +1130 -0
  40. package/dist/elasticsearch/docs/search/DOC.md +1634 -0
  41. package/dist/elevenlabs/docs/text-to-speech/javascript/DOC.md +336 -0
  42. package/dist/elevenlabs/docs/text-to-speech/python/DOC.md +552 -0
  43. package/dist/firebase/docs/auth/DOC.md +1015 -0
  44. package/dist/gemini/docs/genai/javascript/DOC.md +691 -0
  45. package/dist/gemini/docs/genai/python/DOC.md +555 -0
  46. package/dist/github/docs/octokit/DOC.md +1560 -0
  47. package/dist/google/docs/bigquery/javascript/DOC.md +1688 -0
  48. package/dist/google/docs/bigquery/python/DOC.md +1503 -0
  49. package/dist/hubspot/docs/crm/javascript/DOC.md +1805 -0
  50. package/dist/hubspot/docs/crm/python/DOC.md +2033 -0
  51. package/dist/huggingface/docs/transformers/DOC.md +948 -0
  52. package/dist/intercom/docs/messaging/javascript/DOC.md +1844 -0
  53. package/dist/intercom/docs/messaging/python/DOC.md +1797 -0
  54. package/dist/jira/docs/issues/javascript/DOC.md +1420 -0
  55. package/dist/jira/docs/issues/python/DOC.md +1492 -0
  56. package/dist/kafka/docs/streaming/javascript/DOC.md +1671 -0
  57. package/dist/kafka/docs/streaming/python/DOC.md +1464 -0
  58. package/dist/landingai-ade/docs/api/DOC.md +620 -0
  59. package/dist/landingai-ade/docs/sdk/python/DOC.md +489 -0
  60. package/dist/landingai-ade/docs/sdk/typescript/DOC.md +542 -0
  61. package/dist/landingai-ade/skills/SKILL.md +489 -0
  62. package/dist/launchdarkly/docs/feature-flags/javascript/DOC.md +1191 -0
  63. package/dist/launchdarkly/docs/feature-flags/python/DOC.md +1671 -0
  64. package/dist/linear/docs/tracker/DOC.md +1554 -0
  65. package/dist/livekit/docs/realtime/javascript/DOC.md +303 -0
  66. package/dist/livekit/docs/realtime/python/DOC.md +163 -0
  67. package/dist/mailchimp/docs/marketing/DOC.md +1420 -0
  68. package/dist/meilisearch/docs/search/DOC.md +1241 -0
  69. package/dist/microsoft/docs/onedrive/javascript/DOC.md +1421 -0
  70. package/dist/microsoft/docs/onedrive/python/DOC.md +1549 -0
  71. package/dist/mongodb/docs/atlas/DOC.md +2041 -0
  72. package/dist/notion/docs/workspace-api/javascript/DOC.md +1435 -0
  73. package/dist/notion/docs/workspace-api/python/DOC.md +1400 -0
  74. package/dist/okta/docs/identity/javascript/DOC.md +1171 -0
  75. package/dist/okta/docs/identity/python/DOC.md +1401 -0
  76. package/dist/openai/docs/chat/javascript/DOC.md +407 -0
  77. package/dist/openai/docs/chat/python/DOC.md +568 -0
  78. package/dist/paypal/docs/checkout/DOC.md +278 -0
  79. package/dist/pinecone/docs/sdk/javascript/DOC.md +984 -0
  80. package/dist/pinecone/docs/sdk/python/DOC.md +1395 -0
  81. package/dist/plaid/docs/banking/javascript/DOC.md +1163 -0
  82. package/dist/plaid/docs/banking/python/DOC.md +1203 -0
  83. package/dist/playwright-community/skills/login-flows/SKILL.md +108 -0
  84. package/dist/postmark/docs/transactional-email/DOC.md +1168 -0
  85. package/dist/prisma/docs/orm/javascript/DOC.md +1419 -0
  86. package/dist/prisma/docs/orm/python/DOC.md +1317 -0
  87. package/dist/qdrant/docs/vector-search/javascript/DOC.md +1221 -0
  88. package/dist/qdrant/docs/vector-search/python/DOC.md +1653 -0
  89. package/dist/rabbitmq/docs/message-queue/javascript/DOC.md +1193 -0
  90. package/dist/rabbitmq/docs/message-queue/python/DOC.md +1243 -0
  91. package/dist/razorpay/docs/payments/javascript/DOC.md +1219 -0
  92. package/dist/razorpay/docs/payments/python/DOC.md +1330 -0
  93. package/dist/redis/docs/key-value/javascript/DOC.md +1851 -0
  94. package/dist/redis/docs/key-value/python/DOC.md +2054 -0
  95. package/dist/registry.json +2817 -0
  96. package/dist/replicate/docs/model-hosting/DOC.md +1318 -0
  97. package/dist/resend/docs/email/DOC.md +1271 -0
  98. package/dist/salesforce/docs/crm/javascript/DOC.md +1241 -0
  99. package/dist/salesforce/docs/crm/python/DOC.md +1183 -0
  100. package/dist/search-index.json +1 -0
  101. package/dist/sendgrid/docs/email-api/javascript/DOC.md +371 -0
  102. package/dist/sendgrid/docs/email-api/python/DOC.md +656 -0
  103. package/dist/sentry/docs/error-tracking/javascript/DOC.md +1073 -0
  104. package/dist/sentry/docs/error-tracking/python/DOC.md +1309 -0
  105. package/dist/shopify/docs/storefront/DOC.md +457 -0
  106. package/dist/slack/docs/workspace/javascript/DOC.md +933 -0
  107. package/dist/slack/docs/workspace/python/DOC.md +271 -0
  108. package/dist/square/docs/payments/javascript/DOC.md +1855 -0
  109. package/dist/square/docs/payments/python/DOC.md +1728 -0
  110. package/dist/stripe/docs/api/DOC.md +1727 -0
  111. package/dist/stripe/docs/payments/DOC.md +1726 -0
  112. package/dist/stytch/docs/auth/javascript/DOC.md +1813 -0
  113. package/dist/stytch/docs/auth/python/DOC.md +1962 -0
  114. package/dist/supabase/docs/client/DOC.md +1606 -0
  115. package/dist/twilio/docs/messaging/python/DOC.md +469 -0
  116. package/dist/twilio/docs/messaging/typescript/DOC.md +946 -0
  117. package/dist/vercel/docs/platform/DOC.md +1940 -0
  118. package/dist/weaviate/docs/vector-db/javascript/DOC.md +1268 -0
  119. package/dist/weaviate/docs/vector-db/python/DOC.md +1388 -0
  120. package/dist/zendesk/docs/support/javascript/DOC.md +2150 -0
  121. package/dist/zendesk/docs/support/python/DOC.md +2297 -0
  122. package/package.json +22 -6
  123. package/skills/get-api-docs/SKILL.md +84 -0
  124. package/src/commands/annotate.js +83 -0
  125. package/src/commands/build.js +12 -1
  126. package/src/commands/feedback.js +150 -0
  127. package/src/commands/get.js +83 -42
  128. package/src/commands/search.js +7 -0
  129. package/src/index.js +43 -17
  130. package/src/lib/analytics.js +90 -0
  131. package/src/lib/annotations.js +57 -0
  132. package/src/lib/bm25.js +170 -0
  133. package/src/lib/cache.js +69 -6
  134. package/src/lib/config.js +8 -3
  135. package/src/lib/identity.js +99 -0
  136. package/src/lib/registry.js +103 -20
  137. package/src/lib/telemetry.js +86 -0
  138. package/src/mcp/server.js +177 -0
  139. package/src/mcp/tools.js +251 -0
@@ -0,0 +1,1183 @@
1
+ ---
2
+ name: crm
3
+ description: "Salesforce Python SDK (simple-salesforce) coding guidelines for Salesforce REST API interactions"
4
+ metadata:
5
+ languages: "python"
6
+ versions: "1.12.9"
7
+ updated-on: "2026-03-02"
8
+ source: maintainer
9
+ tags: "salesforce,crm,soql,enterprise,api"
10
+ ---
11
+
12
+ # Salesforce Python SDK (simple-salesforce) Coding Guidelines
13
+
14
+ You are a Salesforce API coding expert. Help me with writing code using the Salesforce API calling the official libraries and SDKs.
15
+
16
+ You can find the official SDK documentation and code samples here:
17
+ https://simple-salesforce.readthedocs.io/
18
+
19
+ ## Golden Rule: Use the Correct and Current SDK
20
+
21
+ Always use simple-salesforce to interact with Salesforce APIs. Simple-salesforce is the most widely used Python library for Salesforce REST API interactions.
22
+
23
+ - **Library Name:** simple-salesforce
24
+ - **PyPI Package:** `simple-salesforce`
25
+ - **Alternative Library:** `salesforce-api` (less popular)
26
+ - **Legacy Libraries**: Do not use deprecated or unofficial Salesforce Python packages
27
+
28
+ **Installation:**
29
+
30
+ - **Correct:** `pip install simple-salesforce`
31
+ - **With environment variables support:** `pip install simple-salesforce python-dotenv`
32
+
33
+ **APIs and Usage:**
34
+
35
+ - **Correct:** `from simple_salesforce import Salesforce`
36
+ - **Correct:** `sf = Salesforce(username='...', password='...', security_token='...')`
37
+ - **Correct:** `sf.query("SELECT Id, Name FROM Account")`
38
+ - **Correct:** `sf.Account.create({'Name': 'Test'})`
39
+ - **Incorrect:** Using unofficial Salesforce client libraries
40
+ - **Incorrect:** Direct REST API calls without SDK
41
+
42
+ ## Installation
43
+
44
+ Install simple-salesforce via pip:
45
+
46
+ ```bash
47
+ pip install simple-salesforce
48
+ ```
49
+
50
+ For environment variable support:
51
+
52
+ ```bash
53
+ pip install simple-salesforce python-dotenv
54
+ ```
55
+
56
+ ## Environment Variables
57
+
58
+ Set up your Salesforce credentials using environment variables:
59
+
60
+ ```bash
61
+ export SALESFORCE_USERNAME="user@example.org"
62
+ export SALESFORCE_PASSWORD="your_password"
63
+ export SALESFORCE_SECURITY_TOKEN="your_security_token"
64
+ export SALESFORCE_DOMAIN="login" # or "test" for sandbox
65
+ ```
66
+
67
+ Or create a `.env` file:
68
+
69
+ ```
70
+ SALESFORCE_USERNAME=user@example.org
71
+ SALESFORCE_PASSWORD=your_password
72
+ SALESFORCE_SECURITY_TOKEN=your_security_token
73
+ SALESFORCE_DOMAIN=login
74
+ ```
75
+
76
+ Load environment variables in code:
77
+
78
+ ```python
79
+ import os
80
+ from dotenv import load_dotenv
81
+
82
+ load_dotenv()
83
+
84
+ username = os.getenv('SALESFORCE_USERNAME')
85
+ password = os.getenv('SALESFORCE_PASSWORD')
86
+ security_token = os.getenv('SALESFORCE_SECURITY_TOKEN')
87
+ ```
88
+
89
+ ## Initialization
90
+
91
+ Simple-salesforce requires creating a `Salesforce` instance for all API calls.
92
+
93
+ ### Basic Connection (Username/Password/Token)
94
+
95
+ ```python
96
+ from simple_salesforce import Salesforce
97
+
98
+ sf = Salesforce(
99
+ username='user@example.org',
100
+ password='your_password',
101
+ security_token='your_security_token'
102
+ )
103
+ ```
104
+
105
+ ### Connection with Environment Variables
106
+
107
+ ```python
108
+ import os
109
+ from dotenv import load_dotenv
110
+ from simple_salesforce import Salesforce
111
+
112
+ load_dotenv()
113
+
114
+ sf = Salesforce(
115
+ username=os.getenv('SALESFORCE_USERNAME'),
116
+ password=os.getenv('SALESFORCE_PASSWORD'),
117
+ security_token=os.getenv('SALESFORCE_SECURITY_TOKEN')
118
+ )
119
+ ```
120
+
121
+ ### Connection to Sandbox
122
+
123
+ ```python
124
+ from simple_salesforce import Salesforce
125
+
126
+ sf = Salesforce(
127
+ username='user@example.org',
128
+ password='your_password',
129
+ security_token='your_security_token',
130
+ domain='test' # Use 'test' for sandbox
131
+ )
132
+ ```
133
+
134
+ ### Connection with Custom Domain
135
+
136
+ ```python
137
+ from simple_salesforce import Salesforce
138
+
139
+ sf = Salesforce(
140
+ username='user@example.org',
141
+ password='your_password',
142
+ security_token='your_security_token',
143
+ domain='mydomain' # Your My Domain
144
+ )
145
+ ```
146
+
147
+ ## Authentication
148
+
149
+ ### Username/Password/Token Authentication
150
+
151
+ ```python
152
+ from simple_salesforce import Salesforce
153
+
154
+ sf = Salesforce(
155
+ username='user@example.org',
156
+ password='your_password',
157
+ security_token='your_security_token'
158
+ )
159
+
160
+ print(f"Session ID: {sf.session_id}")
161
+ print(f"Instance URL: {sf.sf_instance}")
162
+ ```
163
+
164
+ ### SalesforceLogin (No Token Required)
165
+
166
+ ```python
167
+ from simple_salesforce import Salesforce, SalesforceLogin
168
+
169
+ session_id, instance = SalesforceLogin(
170
+ username='user@example.org',
171
+ password='your_password'
172
+ )
173
+
174
+ sf = Salesforce(instance=instance, session_id=session_id)
175
+ ```
176
+
177
+ ### Session ID Authentication (OAuth)
178
+
179
+ ```python
180
+ from simple_salesforce import Salesforce
181
+
182
+ sf = Salesforce(
183
+ instance='na1.salesforce.com',
184
+ session_id='your_session_id_from_oauth'
185
+ )
186
+ ```
187
+
188
+ ### OAuth 2.0 Access Token
189
+
190
+ ```python
191
+ from simple_salesforce import Salesforce
192
+
193
+ sf = Salesforce(
194
+ instance_url='https://na1.salesforce.com',
195
+ session_id='your_oauth_access_token'
196
+ )
197
+ ```
198
+
199
+ ### Using Requests Session
200
+
201
+ ```python
202
+ import requests
203
+ from simple_salesforce import Salesforce
204
+
205
+ session = requests.Session()
206
+ session.headers.update({'Custom-Header': 'value'})
207
+
208
+ sf = Salesforce(
209
+ username='user@example.org',
210
+ password='your_password',
211
+ security_token='your_security_token',
212
+ session=session
213
+ )
214
+ ```
215
+
216
+ ## SOQL Queries
217
+
218
+ ### Basic Query
219
+
220
+ ```python
221
+ from simple_salesforce import Salesforce
222
+
223
+ sf = Salesforce(
224
+ username='user@example.org',
225
+ password='your_password',
226
+ security_token='your_security_token'
227
+ )
228
+
229
+ result = sf.query("SELECT Id, Name FROM Account LIMIT 10")
230
+
231
+ print(f"Total records: {result['totalSize']}")
232
+
233
+ for record in result['records']:
234
+ print(f"Account: {record['Name']}")
235
+ ```
236
+
237
+ ### Query with WHERE Clause
238
+
239
+ ```python
240
+ result = sf.query(
241
+ "SELECT Id, Name, Industry FROM Account WHERE Industry = 'Technology'"
242
+ )
243
+
244
+ print(f"Found {result['totalSize']} Technology accounts")
245
+
246
+ for record in result['records']:
247
+ print(f"{record['Name']} - {record['Industry']}")
248
+ ```
249
+
250
+ ### Query with Relationships
251
+
252
+ ```python
253
+ # Parent to child relationship
254
+ result = sf.query(
255
+ "SELECT Id, Name, (SELECT Id, Name FROM Contacts) FROM Account LIMIT 5"
256
+ )
257
+
258
+ for account in result['records']:
259
+ print(f"Account: {account['Name']}")
260
+ if account.get('Contacts'):
261
+ for contact in account['Contacts']['records']:
262
+ print(f" Contact: {contact['Name']}")
263
+ ```
264
+
265
+ ### Query with Child to Parent Relationship
266
+
267
+ ```python
268
+ result = sf.query(
269
+ "SELECT Id, Name, Account.Name FROM Contact LIMIT 10"
270
+ )
271
+
272
+ for contact in result['records']:
273
+ account_name = contact.get('Account', {}).get('Name', 'N/A')
274
+ print(f"{contact['Name']} - Account: {account_name}")
275
+ ```
276
+
277
+ ### Query All (Including Deleted Records)
278
+
279
+ ```python
280
+ result = sf.query_all(
281
+ "SELECT Id, Name, IsDeleted FROM Account WHERE IsDeleted = true"
282
+ )
283
+
284
+ print(f"Deleted accounts: {result['totalSize']}")
285
+ ```
286
+
287
+ ### Query with Automatic Pagination
288
+
289
+ ```python
290
+ # query_all handles pagination automatically
291
+ result = sf.query_all("SELECT Id, Name FROM Account")
292
+
293
+ print(f"Total records: {result['totalSize']}")
294
+
295
+ for record in result['records']:
296
+ print(f"Account: {record['Name']}")
297
+ ```
298
+
299
+ ### Query All Iterator (Memory Efficient)
300
+
301
+ ```python
302
+ # Use query_all_iter for large datasets
303
+ for record in sf.query_all_iter("SELECT Id, Name FROM Account"):
304
+ print(f"Account: {record['Name']}")
305
+ ```
306
+
307
+ ### Manual Pagination
308
+
309
+ ```python
310
+ result = sf.query("SELECT Id, Name FROM Account")
311
+ all_records = result['records']
312
+
313
+ while not result['done']:
314
+ result = sf.query_more(result['nextRecordsUrl'], identifier_is_url=True)
315
+ all_records.extend(result['records'])
316
+
317
+ print(f"Total records retrieved: {len(all_records)}")
318
+ ```
319
+
320
+ ### Safe Query with Parameter Formatting
321
+
322
+ ```python
323
+ from simple_salesforce.format import format_soql
324
+
325
+ # Safe parameter substitution
326
+ last_name = "O'Brien"
327
+ query = format_soql("SELECT Id, Email FROM Contact WHERE LastName = {}", last_name)
328
+ result = sf.query(query)
329
+
330
+ # LIKE queries
331
+ name_pattern = "John"
332
+ query = format_soql(
333
+ "SELECT Id, Name FROM Contact WHERE Name LIKE '{:like}%'",
334
+ name_pattern
335
+ )
336
+ result = sf.query(query)
337
+
338
+ # Multiple parameters
339
+ query = format_soql(
340
+ "SELECT Id, Name FROM Account WHERE Industry = {} AND AnnualRevenue > {}",
341
+ "Technology",
342
+ 1000000
343
+ )
344
+ result = sf.query(query)
345
+ ```
346
+
347
+ ### Query with Date Filters
348
+
349
+ ```python
350
+ from datetime import datetime, timedelta
351
+
352
+ # Query records created in the last 7 days
353
+ seven_days_ago = (datetime.now() - timedelta(days=7)).strftime('%Y-%m-%dT%H:%M:%SZ')
354
+ query = f"SELECT Id, Name FROM Account WHERE CreatedDate > {seven_days_ago}"
355
+ result = sf.query(query)
356
+ ```
357
+
358
+ ## SOSL Search
359
+
360
+ ### Basic Search
361
+
362
+ ```python
363
+ result = sf.search(
364
+ "FIND {United*} IN NAME FIELDS RETURNING Account(Id, Name), Contact(Id, Name)"
365
+ )
366
+
367
+ print(f"Search results: {result['searchRecords']}")
368
+
369
+ for record in result['searchRecords']:
370
+ print(f"{record['attributes']['type']}: {record['Name']}")
371
+ ```
372
+
373
+ ## CRUD Operations
374
+
375
+ ### Create Single Record
376
+
377
+ ```python
378
+ from simple_salesforce import Salesforce
379
+
380
+ sf = Salesforce(
381
+ username='user@example.org',
382
+ password='your_password',
383
+ security_token='your_security_token'
384
+ )
385
+
386
+ result = sf.Account.create({
387
+ 'Name': 'New Account',
388
+ 'Industry': 'Technology',
389
+ 'BillingCity': 'San Francisco'
390
+ })
391
+
392
+ print(f"Created account ID: {result['id']}")
393
+ print(f"Success: {result['success']}")
394
+ ```
395
+
396
+ ### Create with Error Handling
397
+
398
+ ```python
399
+ try:
400
+ result = sf.Account.create({
401
+ 'Name': 'Test Account',
402
+ 'Industry': 'Technology'
403
+ })
404
+ print(f"Created: {result['id']}")
405
+ except Exception as e:
406
+ print(f"Error creating account: {e}")
407
+ ```
408
+
409
+ ### Retrieve Single Record by ID
410
+
411
+ ```python
412
+ account = sf.Account.get('001XXXXXXXXXXXXXXX')
413
+
414
+ print(f"Account Name: {account['Name']}")
415
+ print(f"Industry: {account['Industry']}")
416
+ print(f"Created Date: {account['CreatedDate']}")
417
+ ```
418
+
419
+ ### Retrieve Specific Fields
420
+
421
+ ```python
422
+ account = sf.Account.get('001XXXXXXXXXXXXXXX', fields=['Id', 'Name', 'Industry'])
423
+
424
+ print(f"Name: {account['Name']}")
425
+ print(f"Industry: {account['Industry']}")
426
+ ```
427
+
428
+ ### Update Single Record
429
+
430
+ ```python
431
+ result = sf.Account.update('001XXXXXXXXXXXXXXX', {
432
+ 'Name': 'Updated Account Name',
433
+ 'Industry': 'Media'
434
+ })
435
+
436
+ print(f"Updated: {result}")
437
+ ```
438
+
439
+ ### Update Multiple Fields
440
+
441
+ ```python
442
+ result = sf.Account.update('001XXXXXXXXXXXXXXX', {
443
+ 'Name': 'New Name',
444
+ 'Industry': 'Finance',
445
+ 'BillingCity': 'New York',
446
+ 'Phone': '555-1234'
447
+ })
448
+ ```
449
+
450
+ ### Upsert (Update or Insert)
451
+
452
+ ```python
453
+ # Upsert using external ID
454
+ result = sf.Account.upsert('ExternalId__c/EXT001', {
455
+ 'Name': 'Upserted Account',
456
+ 'Industry': 'Technology',
457
+ 'ExternalId__c': 'EXT001'
458
+ })
459
+
460
+ if result == 204:
461
+ print("Record updated")
462
+ elif result == 201:
463
+ print("Record created")
464
+ ```
465
+
466
+ ### Upsert with Response Details
467
+
468
+ ```python
469
+ try:
470
+ result = sf.Contact.upsert('Email/[email protected]', {
471
+ 'FirstName': 'John',
472
+ 'LastName': 'Doe',
473
+ 'Email': '[email protected]'
474
+ }, raw_response=True)
475
+
476
+ if result.status_code == 201:
477
+ print("Contact created")
478
+ elif result.status_code == 204:
479
+ print("Contact updated")
480
+ except Exception as e:
481
+ print(f"Upsert error: {e}")
482
+ ```
483
+
484
+ ### Delete Single Record
485
+
486
+ ```python
487
+ result = sf.Account.delete('001XXXXXXXXXXXXXXX')
488
+
489
+ print(f"Deleted: {result}")
490
+ ```
491
+
492
+ ### Delete with Error Handling
493
+
494
+ ```python
495
+ try:
496
+ sf.Account.delete('001XXXXXXXXXXXXXXX')
497
+ print("Account deleted successfully")
498
+ except Exception as e:
499
+ print(f"Error deleting account: {e}")
500
+ ```
501
+
502
+ ## Bulk Operations
503
+
504
+ ### Bulk Insert
505
+
506
+ ```python
507
+ from simple_salesforce import Salesforce
508
+
509
+ sf = Salesforce(
510
+ username='user@example.org',
511
+ password='your_password',
512
+ security_token='your_security_token'
513
+ )
514
+
515
+ # Prepare data
516
+ accounts = []
517
+ for i in range(1000):
518
+ accounts.append({
519
+ 'Name': f'Bulk Account {i}',
520
+ 'Industry': 'Technology'
521
+ })
522
+
523
+ # Bulk insert using Bulk 2.0 API
524
+ result = sf.bulk2.Account.insert(accounts)
525
+
526
+ print(f"Job ID: {result['job_id']}")
527
+ print(f"Records processed: {result['numberRecordsProcessed']}")
528
+ ```
529
+
530
+ ### Bulk Insert from CSV
531
+
532
+ ```python
533
+ # Insert from CSV file
534
+ result = sf.bulk2.Account.insert('./accounts.csv')
535
+
536
+ print(f"Job ID: {result['job_id']}")
537
+ print(f"Records processed: {result['numberRecordsProcessed']}")
538
+ ```
539
+
540
+ ### Bulk Update
541
+
542
+ ```python
543
+ # Prepare update data
544
+ updates = []
545
+ for account_id in account_ids:
546
+ updates.append({
547
+ 'Id': account_id,
548
+ 'Industry': 'Finance'
549
+ })
550
+
551
+ result = sf.bulk2.Account.update(updates)
552
+
553
+ print(f"Records updated: {result['numberRecordsProcessed']}")
554
+ ```
555
+
556
+ ### Bulk Update from CSV
557
+
558
+ ```python
559
+ result = sf.bulk2.Account.update('./account_updates.csv')
560
+
561
+ print(f"Job ID: {result['job_id']}")
562
+ ```
563
+
564
+ ### Bulk Delete
565
+
566
+ ```python
567
+ # Prepare delete data (IDs only)
568
+ deletes = []
569
+ for account_id in account_ids_to_delete:
570
+ deletes.append({'Id': account_id})
571
+
572
+ result = sf.bulk2.Account.delete(deletes)
573
+
574
+ print(f"Records deleted: {result['numberRecordsProcessed']}")
575
+ ```
576
+
577
+ ### Bulk Upsert
578
+
579
+ ```python
580
+ upserts = []
581
+ for i in range(100):
582
+ upserts.append({
583
+ 'ExternalId__c': f'EXT{i}',
584
+ 'Name': f'Bulk Upsert Account {i}',
585
+ 'Industry': 'Technology'
586
+ })
587
+
588
+ result = sf.bulk2.Account.upsert(upserts, 'ExternalId__c')
589
+
590
+ print(f"Records processed: {result['numberRecordsProcessed']}")
591
+ ```
592
+
593
+ ### Get Bulk Job Results
594
+
595
+ ```python
596
+ job_id = 'bulk_job_id_from_insert_or_update'
597
+
598
+ # Get successful records
599
+ successful = sf.bulk2.Account.get_successful_records(job_id)
600
+ print(f"Successful records: {successful}")
601
+
602
+ # Get failed records
603
+ failed = sf.bulk2.Account.get_failed_records(job_id)
604
+ print(f"Failed records: {failed}")
605
+
606
+ # Save failed records to CSV
607
+ sf.bulk2.Account.get_failed_records(job_id, file='failed_records.csv')
608
+ ```
609
+
610
+ ### Bulk Query
611
+
612
+ ```python
613
+ # Bulk 2.0 query for large datasets
614
+ result = sf.bulk2.Account.query("SELECT Id, Name, Industry FROM Account")
615
+
616
+ print(f"Job ID: {result['job_id']}")
617
+
618
+ # Get query results
619
+ query_results = sf.bulk2.Account.get_query_results(result['job_id'])
620
+
621
+ for record in query_results:
622
+ print(f"Account: {record['Name']}")
623
+ ```
624
+
625
+ ## SObject Metadata
626
+
627
+ ### Describe SObject
628
+
629
+ ```python
630
+ metadata = sf.Account.describe()
631
+
632
+ print(f"Label: {metadata['label']}")
633
+ print(f"Updateable: {metadata['updateable']}")
634
+ print(f"Deletable: {metadata['deletable']}")
635
+ print(f"Number of fields: {len(metadata['fields'])}")
636
+
637
+ for field in metadata['fields']:
638
+ print(f" {field['name']} ({field['type']})")
639
+ ```
640
+
641
+ ### Describe All SObjects
642
+
643
+ ```python
644
+ result = sf.describe()
645
+
646
+ for sobject in result['sobjects']:
647
+ print(f"{sobject['name']} - {sobject['label']}")
648
+ ```
649
+
650
+ ### Get Field Information
651
+
652
+ ```python
653
+ metadata = sf.Contact.describe()
654
+
655
+ for field in metadata['fields']:
656
+ if field['name'] == 'Email':
657
+ print(f"Type: {field['type']}")
658
+ print(f"Length: {field['length']}")
659
+ print(f"Required: {field['nillable'] == False}")
660
+ print(f"Updateable: {field['updateable']}")
661
+ ```
662
+
663
+ ### Get Picklist Values
664
+
665
+ ```python
666
+ metadata = sf.Account.describe()
667
+
668
+ for field in metadata['fields']:
669
+ if field['name'] == 'Industry' and field['type'] == 'picklist':
670
+ print("Industry picklist values:")
671
+ for value in field['picklistValues']:
672
+ print(f" - {value['value']}")
673
+ ```
674
+
675
+ ## Apex REST API
676
+
677
+ ### Call Custom Apex REST Endpoint (GET)
678
+
679
+ ```python
680
+ # Call GET endpoint
681
+ result = sf.apexecute('MyApexRestService', method='GET')
682
+
683
+ print(f"Response: {result}")
684
+ ```
685
+
686
+ ### Call Custom Apex REST Endpoint (POST)
687
+
688
+ ```python
689
+ payload = {
690
+ 'name': 'Test',
691
+ 'value': 123,
692
+ 'active': True
693
+ }
694
+
695
+ result = sf.apexecute('MyApexRestService', method='POST', data=payload)
696
+
697
+ print(f"Response: {result}")
698
+ ```
699
+
700
+ ### Call Custom Apex REST Endpoint (PUT)
701
+
702
+ ```python
703
+ payload = {
704
+ 'name': 'Updated Name',
705
+ 'value': 456
706
+ }
707
+
708
+ result = sf.apexecute('MyApexRestService/recordId', method='PUT', data=payload)
709
+
710
+ print(f"Response: {result}")
711
+ ```
712
+
713
+ ### Call Custom Apex REST Endpoint (DELETE)
714
+
715
+ ```python
716
+ result = sf.apexecute('MyApexRestService/recordId', method='DELETE')
717
+
718
+ print(f"Response: {result}")
719
+ ```
720
+
721
+ ### Call with URL Parameters
722
+
723
+ ```python
724
+ result = sf.apexecute('MyApexRestService', method='GET', params={'filter': 'active'})
725
+
726
+ print(f"Response: {result}")
727
+ ```
728
+
729
+ ## Generic REST API Requests
730
+
731
+ ### GET Request
732
+
733
+ ```python
734
+ # Make a generic GET request
735
+ result = sf.restful('sobjects/Account/001XXXXXXXXXXXXXXX')
736
+
737
+ print(f"Account: {result}")
738
+ ```
739
+
740
+ ### POST Request
741
+
742
+ ```python
743
+ data = {
744
+ 'Name': 'New Account',
745
+ 'Industry': 'Technology'
746
+ }
747
+
748
+ result = sf.restful('sobjects/Account', method='POST', data=data)
749
+
750
+ print(f"Created: {result}")
751
+ ```
752
+
753
+ ### Custom API Endpoint
754
+
755
+ ```python
756
+ # Call a custom REST endpoint
757
+ result = sf.restful('services/apexrest/CustomEndpoint', method='GET')
758
+
759
+ print(f"Response: {result}")
760
+ ```
761
+
762
+ ## Advanced Features
763
+
764
+ ### Set API Version
765
+
766
+ ```python
767
+ sf = Salesforce(
768
+ username='user@example.org',
769
+ password='your_password',
770
+ security_token='your_security_token',
771
+ version='58.0'
772
+ )
773
+ ```
774
+
775
+ ### Disable SSL Verification (Not Recommended)
776
+
777
+ ```python
778
+ import urllib3
779
+ urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
780
+
781
+ sf = Salesforce(
782
+ username='user@example.org',
783
+ password='your_password',
784
+ security_token='your_security_token',
785
+ security_token_bypass=True,
786
+ session=None
787
+ )
788
+ ```
789
+
790
+ ### Custom Timeout
791
+
792
+ ```python
793
+ import requests
794
+ from simple_salesforce import Salesforce
795
+
796
+ session = requests.Session()
797
+ session.timeout = 60 # 60 seconds
798
+
799
+ sf = Salesforce(
800
+ username='user@example.org',
801
+ password='your_password',
802
+ security_token='your_security_token',
803
+ session=session
804
+ )
805
+ ```
806
+
807
+ ### Proxies
808
+
809
+ ```python
810
+ import requests
811
+ from simple_salesforce import Salesforce
812
+
813
+ session = requests.Session()
814
+ session.proxies = {
815
+ 'http': 'http://proxy.example.com:8080',
816
+ 'https': 'https://proxy.example.com:8080'
817
+ }
818
+
819
+ sf = Salesforce(
820
+ username='user@example.org',
821
+ password='your_password',
822
+ security_token='your_security_token',
823
+ session=session
824
+ )
825
+ ```
826
+
827
+ ### Custom Headers
828
+
829
+ ```python
830
+ import requests
831
+ from simple_salesforce import Salesforce
832
+
833
+ session = requests.Session()
834
+ session.headers.update({
835
+ 'Custom-Header': 'value',
836
+ 'Another-Header': 'another-value'
837
+ })
838
+
839
+ sf = Salesforce(
840
+ username='user@example.org',
841
+ password='your_password',
842
+ security_token='your_security_token',
843
+ session=session
844
+ )
845
+ ```
846
+
847
+ ## Composite API
848
+
849
+ ### Composite Request
850
+
851
+ ```python
852
+ composite_request = {
853
+ 'compositeRequest': [
854
+ {
855
+ 'method': 'POST',
856
+ 'url': '/services/data/v58.0/sobjects/Account',
857
+ 'referenceId': 'NewAccount',
858
+ 'body': {
859
+ 'Name': 'Composite Account'
860
+ }
861
+ },
862
+ {
863
+ 'method': 'POST',
864
+ 'url': '/services/data/v58.0/sobjects/Contact',
865
+ 'referenceId': 'NewContact',
866
+ 'body': {
867
+ 'FirstName': 'John',
868
+ 'LastName': 'Doe',
869
+ 'AccountId': '@{NewAccount.id}'
870
+ }
871
+ }
872
+ ]
873
+ }
874
+
875
+ result = sf.restful(
876
+ 'composite',
877
+ method='POST',
878
+ data=composite_request
879
+ )
880
+
881
+ print(f"Composite results: {result}")
882
+ ```
883
+
884
+ ## Working with Attachments
885
+
886
+ ### Upload Attachment
887
+
888
+ ```python
889
+ import base64
890
+
891
+ with open('document.pdf', 'rb') as f:
892
+ file_data = base64.b64encode(f.read()).decode()
893
+
894
+ result = sf.Attachment.create({
895
+ 'Name': 'document.pdf',
896
+ 'ParentId': '001XXXXXXXXXXXXXXX', # Record ID
897
+ 'Body': file_data,
898
+ 'ContentType': 'application/pdf'
899
+ })
900
+
901
+ print(f"Attachment ID: {result['id']}")
902
+ ```
903
+
904
+ ### Download Attachment
905
+
906
+ ```python
907
+ import base64
908
+
909
+ attachment = sf.Attachment.get('00PXXXXXXXXXXXXXXX')
910
+ file_data = base64.b64decode(attachment['Body'])
911
+
912
+ with open('downloaded_file.pdf', 'wb') as f:
913
+ f.write(file_data)
914
+ ```
915
+
916
+ ## Working with ContentVersion (Files)
917
+
918
+ ### Upload File
919
+
920
+ ```python
921
+ import base64
922
+
923
+ with open('document.pdf', 'rb') as f:
924
+ file_data = base64.b64encode(f.read()).decode()
925
+
926
+ result = sf.ContentVersion.create({
927
+ 'Title': 'My Document',
928
+ 'PathOnClient': 'document.pdf',
929
+ 'VersionData': file_data,
930
+ 'FirstPublishLocationId': '001XXXXXXXXXXXXXXX' # Record ID
931
+ })
932
+
933
+ print(f"ContentVersion ID: {result['id']}")
934
+ ```
935
+
936
+ ### Query Files
937
+
938
+ ```python
939
+ result = sf.query(
940
+ "SELECT Id, Title, FileType FROM ContentDocument WHERE Title LIKE '%Report%'"
941
+ )
942
+
943
+ for doc in result['records']:
944
+ print(f"Document: {doc['Title']} ({doc['FileType']})")
945
+ ```
946
+
947
+ ## Error Handling
948
+
949
+ ### Basic Error Handling
950
+
951
+ ```python
952
+ from simple_salesforce import Salesforce, SalesforceAuthenticationFailed
953
+
954
+ try:
955
+ sf = Salesforce(
956
+ username='user@example.org',
957
+ password='wrong_password',
958
+ security_token='token'
959
+ )
960
+ except SalesforceAuthenticationFailed as e:
961
+ print(f"Authentication failed: {e}")
962
+ ```
963
+
964
+ ### CRUD Error Handling
965
+
966
+ ```python
967
+ from simple_salesforce.exceptions import SalesforceError
968
+
969
+ try:
970
+ result = sf.Account.create({
971
+ 'Name': '' # Required field missing
972
+ })
973
+ except SalesforceError as e:
974
+ print(f"Error code: {e.status}")
975
+ print(f"Error message: {e.content}")
976
+ ```
977
+
978
+ ### Query Error Handling
979
+
980
+ ```python
981
+ try:
982
+ result = sf.query("SELECT Id, InvalidField FROM Account")
983
+ except SalesforceError as e:
984
+ print(f"Query error: {e}")
985
+ ```
986
+
987
+ ### Generic Exception Handling
988
+
989
+ ```python
990
+ import requests
991
+
992
+ try:
993
+ result = sf.Account.update('001XXXXXXXXXXXXXXX', {
994
+ 'Name': 'Updated Name'
995
+ })
996
+ except requests.exceptions.Timeout:
997
+ print("Request timed out")
998
+ except requests.exceptions.ConnectionError:
999
+ print("Connection error")
1000
+ except Exception as e:
1001
+ print(f"Unexpected error: {e}")
1002
+ ```
1003
+
1004
+ ## Best Practices
1005
+
1006
+ ### Batch Processing
1007
+
1008
+ ```python
1009
+ def process_accounts_in_batches(account_ids, batch_size=200):
1010
+ for i in range(0, len(account_ids), batch_size):
1011
+ batch = account_ids[i:i + batch_size]
1012
+
1013
+ # Build query for batch
1014
+ ids_str = "','".join(batch)
1015
+ query = f"SELECT Id, Name FROM Account WHERE Id IN ('{ids_str}')"
1016
+
1017
+ result = sf.query(query)
1018
+
1019
+ for record in result['records']:
1020
+ # Process each record
1021
+ print(f"Processing: {record['Name']}")
1022
+ ```
1023
+
1024
+ ### Retry Logic
1025
+
1026
+ ```python
1027
+ import time
1028
+ from simple_salesforce.exceptions import SalesforceError
1029
+
1030
+ def create_with_retry(sobject, data, max_retries=3):
1031
+ for attempt in range(max_retries):
1032
+ try:
1033
+ result = getattr(sf, sobject).create(data)
1034
+ return result
1035
+ except SalesforceError as e:
1036
+ if attempt < max_retries - 1:
1037
+ wait_time = 2 ** attempt # Exponential backoff
1038
+ print(f"Retry {attempt + 1} after {wait_time}s")
1039
+ time.sleep(wait_time)
1040
+ else:
1041
+ raise
1042
+ ```
1043
+
1044
+ ### Connection Pooling
1045
+
1046
+ ```python
1047
+ import requests
1048
+ from requests.adapters import HTTPAdapter
1049
+ from urllib3.util.retry import Retry
1050
+ from simple_salesforce import Salesforce
1051
+
1052
+ def create_sf_session():
1053
+ session = requests.Session()
1054
+
1055
+ retry_strategy = Retry(
1056
+ total=3,
1057
+ status_forcelist=[429, 500, 502, 503, 504],
1058
+ method_whitelist=["HEAD", "GET", "OPTIONS", "POST", "PATCH", "PUT"]
1059
+ )
1060
+
1061
+ adapter = HTTPAdapter(max_retries=retry_strategy)
1062
+ session.mount("https://", adapter)
1063
+ session.mount("http://", adapter)
1064
+
1065
+ return session
1066
+
1067
+ sf = Salesforce(
1068
+ username='user@example.org',
1069
+ password='your_password',
1070
+ security_token='your_security_token',
1071
+ session=create_sf_session()
1072
+ )
1073
+ ```
1074
+
1075
+ ### Efficient Field Selection
1076
+
1077
+ ```python
1078
+ # Only query fields you need
1079
+ result = sf.query(
1080
+ "SELECT Id, Name, Industry FROM Account LIMIT 100"
1081
+ )
1082
+
1083
+ # Instead of:
1084
+ # result = sf.query("SELECT Id, Name, Industry, ... (all fields) FROM Account LIMIT 100")
1085
+ ```
1086
+
1087
+ ### Use Bulk API for Large Data Sets
1088
+
1089
+ ```python
1090
+ # For < 2000 records, use regular API
1091
+ if len(records) < 2000:
1092
+ for record in records:
1093
+ sf.Account.create(record)
1094
+ else:
1095
+ # For >= 2000 records, use Bulk API
1096
+ sf.bulk2.Account.insert(records)
1097
+ ```
1098
+
1099
+ ## Complete Example Application
1100
+
1101
+ ```python
1102
+ import os
1103
+ from dotenv import load_dotenv
1104
+ from simple_salesforce import Salesforce, SalesforceAuthenticationFailed
1105
+ from simple_salesforce.exceptions import SalesforceError
1106
+
1107
+ # Load environment variables
1108
+ load_dotenv()
1109
+
1110
+ def main():
1111
+ try:
1112
+ # Initialize connection
1113
+ sf = Salesforce(
1114
+ username=os.getenv('SALESFORCE_USERNAME'),
1115
+ password=os.getenv('SALESFORCE_PASSWORD'),
1116
+ security_token=os.getenv('SALESFORCE_SECURITY_TOKEN')
1117
+ )
1118
+
1119
+ print("Connected to Salesforce successfully!")
1120
+
1121
+ # Create account
1122
+ account_result = sf.Account.create({
1123
+ 'Name': 'Example Corp',
1124
+ 'Industry': 'Technology',
1125
+ 'BillingCity': 'San Francisco'
1126
+ })
1127
+ account_id = account_result['id']
1128
+ print(f"Created account: {account_id}")
1129
+
1130
+ # Create contact
1131
+ contact_result = sf.Contact.create({
1132
+ 'FirstName': 'John',
1133
+ 'LastName': 'Doe',
1134
+ 'Email': '[email protected]',
1135
+ 'AccountId': account_id
1136
+ })
1137
+ contact_id = contact_result['id']
1138
+ print(f"Created contact: {contact_id}")
1139
+
1140
+ # Query accounts with contacts
1141
+ result = sf.query(f"""
1142
+ SELECT Id, Name, Industry,
1143
+ (SELECT Id, Name, Email FROM Contacts)
1144
+ FROM Account
1145
+ WHERE Id = '{account_id}'
1146
+ """)
1147
+
1148
+ for account in result['records']:
1149
+ print(f"\nAccount: {account['Name']}")
1150
+ if account.get('Contacts'):
1151
+ for contact in account['Contacts']['records']:
1152
+ print(f" Contact: {contact['Name']} - {contact['Email']}")
1153
+
1154
+ # Update account
1155
+ sf.Account.update(account_id, {
1156
+ 'Industry': 'Finance'
1157
+ })
1158
+ print(f"\nUpdated account industry")
1159
+
1160
+ # Clean up - delete records
1161
+ sf.Contact.delete(contact_id)
1162
+ sf.Account.delete(account_id)
1163
+ print("\nCleaned up test data")
1164
+
1165
+ except SalesforceAuthenticationFailed as e:
1166
+ print(f"Authentication failed: {e}")
1167
+ except SalesforceError as e:
1168
+ print(f"Salesforce API error: {e}")
1169
+ except Exception as e:
1170
+ print(f"Unexpected error: {e}")
1171
+
1172
+ if __name__ == '__main__':
1173
+ main()
1174
+ ```
1175
+
1176
+ ## Useful Links
1177
+
1178
+ - Documentation: https://simple-salesforce.readthedocs.io/
1179
+ - GitHub: https://github.com/simple-salesforce/simple-salesforce
1180
+ - PyPI: https://pypi.org/project/simple-salesforce/
1181
+ - Salesforce Developer Docs: https://developer.salesforce.com/
1182
+ - Salesforce REST API Guide: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/
1183
+ - SOQL Reference: https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/