Salesforce External App Configuration for Bulk API v2¶
Data: 2025-12-28 Scope: Configurazione Connected App per supportare Bulk API v2 Domanda: Serve una External App separata per Bulk API?
π― RISPOSTA RAPIDA¶
Serve creare una nuova External App?¶
β NO - Puoi usare la stessa Connected App esistente
MA devi aggiungere:
- β OAuth Scope "full" (o "web")
- β User Permission "Bulk API Hard Delete"
- β User Permission "Modify All Data"
- β Rigenerare il JWT token dopo le modifiche
π CONFIGURAZIONE ATTUALE vs BULK API v2¶
β Configurazione Attuale (REST API funzionante)¶
Tua Connected App:
Connected App: Kinetic-Core
βββ OAuth Scopes
β βββ api β
Funziona per REST
β βββ refresh_token β
Funziona per refresh
β
βββ JWT Bearer Flow: Enabled
βββ Certificate: Installato
βββ Permessi Base: API Enabled
Funziona per: - β REST API standard - β Composite API (batch <200) - β Query/SOQL - β CRUD operations
NON funziona per: - β Bulk API v2 jobs - β Upload CSV massivi - β Processi asincroni bulk
π¨ MODIFICHE NECESSARIE PER BULK API v2¶
1οΈβ£ OAuth Scopes (CRITICO!)¶
β Configurazione Attuale (insufficiente)¶
Problema: Salesforce restituisce 403 Forbidden su /jobs/ingest
β Configurazione Corretta (Bulk API v2)¶
OAuth Scopes richiesti:
- api # β
REST API base
- refresh_token # β
Token refresh
- full # β OBBLIGATORIO per Bulk API v2
ALTERNATIVA (piΓΉ granulare):
OAuth Scopes alternativi:
- api # β
REST API base
- refresh_token # β
Token refresh
- web # β Include accesso Bulk API
β οΈ ATTENZIONE: Rigenerare Token!¶
# Dopo aver modificato gli scope OAuth nella Connected App:
1. Salva modifiche in Salesforce
2. Attendi 2-10 minuti (propagazione cache Salesforce)
3. Rigenera un nuovo JWT token
4. NON riutilizzare token cached/salvati
# I token esistenti NON acquisiscono automaticamente i nuovi scope!
2οΈβ£ User Permissions (Profile/Permission Set)¶
β Permessi Attuali (REST API)¶
β Permessi Aggiuntivi per Bulk API v2¶
User Permissions OBBLIGATORI per Bulk:
- API Enabled β
GiΓ presente
- Bulk API Hard Delete β NUOVO - Obbligatorio
- View All Data β NUOVO - Per query bulk
- Modify All Data β NUOVO - Per insert/update/delete bulk
Dove configurarli:
Setup β Users β Permission Sets β Create New
Name: Bulk API Access
API Name: Bulk_API_Access
System Permissions:
β API Enabled
β Bulk API Hard Delete β CRITICO
β View All Data β Per query bulk
β Modify All Data β Per operazioni bulk
Assigned Users:
β Aggiungi l'utente JWT/OAuth
3οΈβ£ Object-Level Security (OLS)¶
Per ogni oggetto usato con Bulk API:¶
Setup β Object Manager β Account (esempio)
Object Permissions richiesti:
- Read β
Per query
- Create β
Per insert/upsert
- Edit β
Per update/upsert
- Delete β
Per delete
- View All β CONSIGLIATO per Bulk
- Modify All β CONSIGLIATO per Bulk
Configurazione tramite Permission Set:
Permission Set β Object Settings β Account
β Read
β Create
β Edit
β Delete
β View All Records β Evita SOQL restrictions
β Modify All Records β Evita sharing restrictions
π§ GUIDA STEP-BY-STEP¶
Step 1: Modifica Connected App Esistente¶
Setup β App Manager β [Your Connected App] β Edit
ββββββββββββββββββββββββββββββββββββββββββββββββββ
Section: OAuth Policies
Selected OAuth Scopes:
Disponibili Selezionati
ββββββββββββββββ βββββββββββββββββββ
Access and manage data (api) β [MOVE] β api β
Perform requests at any time β [MOVE] β refresh_token β
Full access (full) β [MOVE] β full β NUOVO
ββββββββββββββββββββββββββββββββββββββββββββββββββ
Section: Permitted Users
β Admin approved users are pre-authorized
Section: IP Relaxation
Development: β Relax IP restrictions
Production: β Enforce IP restrictions (whitelist)
ββββββββββββββββββββββββββββββββββββββββββββββββββ
[Save]
β± Tempo di propagazione: 2-10 minuti
Step 2: Crea Permission Set per Bulk API¶
Setup β Permission Sets β New
ββββββββββββββββββββββββββββββββββββββββββββββββββ
Label: Bulk API Access
API Name: Bulk_API_Access
License: --None--
ββββββββββββββββββββββββββββββββββββββββββββββββββ
[Save] β [System Permissions]
System Permissions to Enable:
β API Enabled
β Bulk API Hard Delete β CRITICO
β View All Data β Per query bulk
β Modify All Data β Per operazioni bulk
[Save]
ββββββββββββββββββββββββββββββββββββββββββββββββββ
[Object Settings] β Edit β Account
Account Object Permissions:
β Read
β Create
β Edit
β Delete
β View All Records β Bulk queries
β Modify All Records β Bulk operations
[Save]
ββββββββββββββββββββββββββββββββββββββββββββββββββ
[Manage Assignments] β Add Assignments
Select Users:
β [Your JWT/OAuth User]
[Assign]
Step 3: Rigenera JWT Token¶
# Attendi 5-10 minuti dopo modifiche scope
# Poi rigenera token con kinetic-core:
from kinetic_core import JWTAuthenticator
auth = JWTAuthenticator.from_env()
session = auth.authenticate() # Nuovo token con scope "full"
print(f"New token generated: {session.access_token[:30]}...")
print(f"Instance URL: {session.instance_url}")
β οΈ IMPORTANTE: - Elimina token cached - Non riutilizzare vecchi token - Il nuovo token contiene i nuovi scope
Step 4: Test Bulk API Access¶
import requests
# Test creazione job Bulk API v2
headers = {
"Authorization": f"Bearer {session.access_token}",
"Content-Type": "application/json"
}
url = f"{session.instance_url}/services/data/v62.0/jobs/ingest"
response = requests.post(
url,
headers=headers,
json={
"object": "Account",
"operation": "insert",
"contentType": "CSV"
}
)
print(f"Status: {response.status_code}")
if response.status_code == 201:
print("β
Bulk API v2 WORKS!")
job = response.json()
print(f"Job ID: {job['id']}")
print(f"State: {job['state']}")
# Cleanup: abort test job
requests.patch(
f"{url}/{job['id']}",
headers=headers,
json={"state": "Aborted"}
)
print("Test job aborted")
elif response.status_code == 403:
print("β FAILED: Missing permissions")
print("β Add 'Bulk API Hard Delete' permission")
print("β Add 'Modify All Data' permission")
elif response.status_code == 400 and "invalid_grant" in response.text:
print("β FAILED: OAuth scope issue")
print("β Add 'full' scope to Connected App")
print("β Wait 5-10 minutes")
print("β Regenerate JWT token")
else:
print(f"β Error: {response.text}")
Output atteso:
π TROUBLESHOOTING¶
Errore: 403 Forbidden¶
Diagnosi: Mancano permessi utente
Soluzione: 1. β Verifica Permission Set assegnato 2. β Aggiungi "Bulk API Hard Delete" 3. β Aggiungi "Modify All Data" 4. β Verifica Object Permissions (View All, Modify All)
Errore: 400 invalid_grant¶
Diagnosi: Scope OAuth insufficienti o token non aggiornato
Soluzione: 1. β Aggiungi scope "full" alla Connected App 2. β Attendi 5-10 minuti (propagazione) 3. β Rigenera nuovo JWT token 4. β NON usare token cached
Errore: 404 Not Found¶
Diagnosi: Endpoint sbagliato o API version troppo vecchia
Soluzione:
1. β
Usa API version >= v52.0
2. β
Endpoint corretto: /services/data/v62.0/jobs/ingest
3. β
Non confondere con Bulk API v1 (/async/)
Errore: Token funziona per REST ma non per Bulk¶
Diagnosi: Token generato prima di aggiungere scope "full"
Soluzione:
# 1. Verifica scope nella Connected App
Setup β App Manager β [App] β View β OAuth Scopes
# Deve contenere "full" o "web"
# 2. Elimina token cached
rm .token_cache # o equivalente
# 3. Rigenera token
python -c "from kinetic_core import JWTAuthenticator;
auth = JWTAuthenticator.from_env();
auth.authenticate()"
π CHECKLIST COMPLETA¶
β Connected App¶
- [ ] OAuth Scope "api" presente
- [ ] OAuth Scope "refresh_token" presente
- [ ] OAuth Scope "full" o "web" aggiunto β NUOVO
- [ ] Certificate JWT configurato
- [ ] Permitted Users configurati
- [ ] IP Relaxation configurata
β Permission Set¶
- [ ] Permission Set "Bulk API Access" creato
- [ ] System Permission: API Enabled
- [ ] System Permission: Bulk API Hard Delete β NUOVO
- [ ] System Permission: View All Data β NUOVO
- [ ] System Permission: Modify All Data β NUOVO
- [ ] Permission Set assegnato all'utente JWT
β Object Permissions (per ogni oggetto)¶
- [ ] Read permission
- [ ] Create permission
- [ ] Edit permission
- [ ] Delete permission
- [ ] View All Records β CONSIGLIATO
- [ ] Modify All Records β CONSIGLIATO
β Testing¶
- [ ] Atteso 5-10 minuti dopo modifiche
- [ ] Rigenerato JWT token
- [ ] Testato endpoint:
POST /jobs/ingest - [ ] Ottenuto risposta 201 Created
- [ ] Verificato Job ID valido
π‘ UNA APP o DUE APP?¶
Opzione 1: Una Sola App (β CONSIGLIATO)¶
Connected App: Kinetic-Core
OAuth Scopes:
- api # REST API
- refresh_token # Refresh
- full # Bulk API v2
Permessi Utente:
- API Enabled
- Bulk API Hard Delete
- View All Data
- Modify All Data
Vantaggi: - β Gestione semplificata - β Un solo token per tutto - β Meno configurazione - β Stesso auth flow
Quando usare: - β La maggior parte dei casi - β Sviluppo e test - β Applicazioni che usano sia REST che Bulk
Opzione 2: Due App Separate (β οΈ Solo casi specifici)¶
App 1: Kinetic-Core-REST
OAuth Scopes: [api, refresh_token]
Uso: CRUD, Query, Composite
App 2: Kinetic-Core-Bulk
OAuth Scopes: [api, refresh_token, full]
Uso: Solo Bulk API v2
Quando usare: - β οΈ Policy di sicurezza che separano REST da Bulk - β οΈ Audit trail separati richiesti - β οΈ Team diversi gestiscono REST e Bulk - β οΈ Limiti API da separare
Svantaggi: - β Doppia gestione - β Due token da gestire - β PiΓΉ complessitΓ - β PiΓΉ certificati/configurazioni
π― BEST PRACTICE PRODUCTION¶
Configurazione Raccomandata (Una App)¶
ββββββββββββββββββββββββββββββββββββββββββββββββββ
Connected App: Kinetic-Core
ββββββββββββββββββββββββββββββββββββββββββββββββββ
OAuth Configuration:
Scopes:
- api # Base REST API
- refresh_token # Token refresh
- full # Bulk API v2 + advanced features
Permitted Users:
- Admin approved users are pre-authorized
IP Restrictions:
Development: Relaxed
Production: Enforced (whitelist IP ranges)
ββββββββββββββββββββββββββββββββββββββββββββββββββ
Permission Set: Kinetic Core API Access
ββββββββββββββββββββββββββββββββββββββββββββββββββ
System Permissions:
β
API Enabled
β
Bulk API Hard Delete
β
View All Data
β
Modify All Data
Object Permissions (Account, Contact, etc):
β
Read
β
Create
β
Edit
β
Delete
β
View All Records
β
Modify All Records
Assigned To:
- JWT Service User (lantoniotrento343@agentforce.com)
π§ͺ SCRIPT DI VERIFICA AUTOMATICA¶
Salva come: tests/verify_bulk_config.py
#!/usr/bin/env python3
"""
Verify Salesforce configuration for Bulk API v2.
Tests:
1. JWT Authentication
2. Bulk API endpoint access
3. Job creation
4. Permissions check
"""
import requests
import sys
from kinetic_core import JWTAuthenticator
def test_authentication():
"""Test JWT authentication."""
print("\n1οΈβ£ Testing JWT Authentication...")
try:
auth = JWTAuthenticator.from_env()
session = auth.authenticate()
print(f" β
Authenticated: {session.instance_url}")
print(f" β
User: {auth.username}")
print(f" β
API Version: {session.api_version}")
return session
except Exception as e:
print(f" β FAILED: {e}")
return None
def test_bulk_api_access(session):
"""Test Bulk API v2 endpoint access."""
print("\n2οΈβ£ Testing Bulk API v2 Access...")
headers = {
"Authorization": f"Bearer {session.access_token}",
"Content-Type": "application/json"
}
url = f"{session.instance_url}/services/data/{session.api_version}/jobs/ingest"
# Create test job
response = requests.post(
url,
headers=headers,
json={
"object": "Account",
"operation": "insert",
"contentType": "CSV"
}
)
if response.status_code == 201:
job = response.json()
print(f" β
Bulk API v2 accessible")
print(f" β
Job created: {job['id']}")
print(f" β
State: {job['state']}")
# Cleanup: abort test job
requests.patch(
f"{url}/{job['id']}",
headers=headers,
json={"state": "Aborted"}
)
print(f" β
Test job aborted")
return True
else:
print(f" β FAILED: HTTP {response.status_code}")
print(f" Error: {response.text[:200]}")
# Diagnose
if response.status_code == 403:
print("\n π‘ Missing permissions:")
print(" β Add 'Bulk API Hard Delete' to Permission Set")
print(" β Add 'Modify All Data' to Permission Set")
print(" β Verify Permission Set is assigned to user")
elif "invalid_grant" in response.text:
print("\n π‘ OAuth scope issue:")
print(" β Add 'full' scope to Connected App")
print(" β Wait 5-10 minutes for propagation")
print(" β Regenerate JWT token")
elif response.status_code == 404:
print("\n π‘ Endpoint issue:")
print(" β Check API version >= v52.0")
print(" β Verify endpoint: /jobs/ingest")
return False
def test_permissions(session):
"""Test user permissions."""
print("\n3οΈβ£ Testing User Permissions...")
# This is informational - we can't directly query permissions via API
# but we infer from Bulk API access test
print(" βΉοΈ Permission verification via Bulk API test")
print(" βΉοΈ If Bulk API works, permissions are correct")
return True
def main():
"""Run all verification tests."""
print("=" * 70)
print(" Salesforce Bulk API v2 Configuration Verification")
print("=" * 70)
# Test 1: Authentication
session = test_authentication()
if not session:
print("\nβ Authentication failed. Fix .env configuration.")
return 1
# Test 2: Bulk API Access
bulk_works = test_bulk_api_access(session)
if not bulk_works:
print("\nβ Bulk API not accessible. Check configuration above.")
return 1
# Test 3: Permissions
test_permissions(session)
# Summary
print("\n" + "=" * 70)
print(" β
ALL TESTS PASSED")
print("=" * 70)
print("\n Salesforce is correctly configured for Bulk API v2!")
print("\n Connected App Scopes: β
")
print(" User Permissions: β
")
print(" Bulk API Access: β
")
print("\n You can now implement Bulk API v2 in kinetic-core.")
print("=" * 70)
return 0
if __name__ == "__main__":
sys.exit(main())
Esegui:
Output atteso (successo):
======================================================================
Salesforce Bulk API v2 Configuration Verification
======================================================================
1οΈβ£ Testing JWT Authentication...
β
Authenticated: https://your-instance.salesforce.com
β
User: your-user@example.com
β
API Version: v62.0
2οΈβ£ Testing Bulk API v2 Access...
β
Bulk API v2 accessible
β
Job created: 750XXXXXXXXXXXXXXX
β
State: Open
β
Test job aborted
3οΈβ£ Testing User Permissions...
βΉοΈ Permission verification via Bulk API test
βΉοΈ If Bulk API works, permissions are correct
======================================================================
β
ALL TESTS PASSED
======================================================================
Salesforce is correctly configured for Bulk API v2!
Connected App Scopes: β
User Permissions: β
Bulk API Access: β
You can now implement Bulk API v2 in kinetic-core.
======================================================================
π CONFIGURAZIONE FINALE RIASSUNTA¶
Prima (REST API only)¶
Connected App OAuth Scopes:
- api
- refresh_token
User Permissions:
- API Enabled
Funziona:
β
REST API
β
Composite API (<200 records)
β Bulk API v2
Dopo (REST + Bulk API v2)¶
Connected App OAuth Scopes:
- api
- refresh_token
- full β AGGIUNTO
User Permissions:
- API Enabled
- Bulk API Hard Delete β AGGIUNTO
- View All Data β AGGIUNTO
- Modify All Data β AGGIUNTO
Funziona:
β
REST API
β
Composite API
β
Bulk API v2 β NUOVO
β CONCLUSIONE¶
Risposta alla Domanda Originale¶
Serve una External App separata per Bulk API v2?
β NO - Una sola Connected App Γ¨ sufficiente e consigliata
Cosa Serve Fare¶
- β Aggiungere scope OAuth "full" alla Connected App esistente
- β Creare Permission Set con permessi Bulk API
- β Assegnare Permission Set all'utente JWT
- β Rigenerare JWT token (IMPORTANTE!)
- β Testare con script di verifica
Tempo Richiesto¶
- Modifica Connected App: 5 minuti
- Creazione Permission Set: 10 minuti
- Propagazione Salesforce: 5-10 minuti
- Test e verifica: 5 minuti
Totale: ~30 minuti
Benefici¶
- β Un solo token per REST e Bulk
- β Configurazione piΓΉ semplice
- β Meno overhead di gestione
- β Performance 20-50x migliori su grandi volumi
Documento creato: 2025-12-28 Configurazione testata: Salesforce Developer Edition Kinetic Core version: 1.1.0 Status: β Pronto per implementazione Bulk API v2
bene ora rileggi l'intero workspace
oltre kinetic-core ho aggiunto kinetincmcp
praticamente l'aggiornamento di Kinetic-Core serve per essere integrato in kineticmcp
quindi leggi la codebase di kineticmcp e dimmi gli impatti generati da questo upgrade
e fammi capire quanti e quali step di implementazione dovremo compiere per avere l'aggiornamento bulk api v2 completo anzi direi tutte le funzionalitΓ possibili per connttere il server mcp a salesforce