Skip to content

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:

  1. βœ… OAuth Scope "full" (o "web")
  2. βœ… User Permission "Bulk API Hard Delete"
  3. βœ… User Permission "Modify All Data"
  4. βœ… 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)

OAuth Scopes:
  - api              # βœ… REST funziona
  - refresh_token    # βœ… Refresh funziona

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)

User Permissions (minimo per REST):
  - API Enabled                     βœ… Base
  - View Setup and Configuration    βœ… Setup

βœ… 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:

Status: 201
βœ… Bulk API v2 WORKS!
Job ID: 750XXXXXXXXXXXXXXX
State: Open
Test job aborted

πŸ” TROUBLESHOOTING

Errore: 403 Forbidden

{
  "errorCode": "INSUFFICIENT_ACCESS",
  "message": "Insufficient privileges"
}

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

{
  "error": "invalid_grant",
  "error_description": "user hasn't approved this consumer"
}

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

{
  "errorCode": "NOT_FOUND",
  "message": "The requested resource does not exist"
}

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:

python tests/verify_bulk_config.py

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

  1. βœ… Aggiungere scope OAuth "full" alla Connected App esistente
  2. βœ… Creare Permission Set con permessi Bulk API
  3. βœ… Assegnare Permission Set all'utente JWT
  4. βœ… Rigenerare JWT token (IMPORTANTE!)
  5. βœ… 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