Agent API - Headless Communication
Interact with your deployed Agentforce agents programmatically using REST APIs. Perfect for building custom integrations, testing workflows, or automating agent interactions.
API Usage Guide
Follow these steps to interact with your agent programmatically:
Step 0: Get OAuth Access Token
First, get an OAuth access token using your credentials. Copy the access_token from the response.
curl -X POST "https://myorg.my.salesforce.com/services/oauth2/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET"
Response example:
{
"access_token": "00D5e0000...",
"instance_url": "https://your-org.my.salesforce.com",
"id": "https://login.salesforce.com/id/...",
"token_type": "Bearer",
"issued_at": "1234567890",
"signature": "..."
}
Step 1: Start Agent Session
Start a new agent session. Save the sessionId from the response.
curl -X POST "https://api.salesforce.com/einstein/ai-agent/v1/agents/YOUR_AGENT_ID/sessions" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"externalSessionKey": "session_1234567890",
"instanceConfig": {
"endpoint": "https://myorg.my.salesforce.com"
},
"tz": "America/Los_Angeles",
"variables": [{
"name": "$Context.EndUserLanguage",
"type": "Text",
"value": "en_US"
}],
"bypassUser": true
}'
Key parameters:
externalSessionKey: A unique identifier for this session (use timestamps or UUIDs)instanceConfig.endpoint: Your Salesforce instance URLtz: Timezone for the sessionvariables: Context variables for the agentbypassUser: Set totruefor headless API access
Response example:
{
"sessionId": "0Xx5e0000...",
"createdDate": "2024-01-01T00:00:00.000Z",
"status": "ACTIVE"
}
Step 2: Send Message to Agent
Send a message to the agent. Increment sequenceId for each new message (1, 2, 3, ...).
curl -X POST "https://api.salesforce.com/einstein/ai-agent/v1/sessions/YOUR_SESSION_ID/messages" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"message": {
"sequenceId": 1,
"type": "Text",
"text": "Hello! Can you help me?"
}
}'
Important: Track the sequenceId yourself - it must increment with each message in the session.
Response example:
{
"messages": [
{
"sequenceId": 1,
"type": "Text",
"text": "Hello! I'm here to help. What can I assist you with today?",
"role": "Agent"
}
]
}
Step 3: End Agent Session
Close the session when done to free up resources.
curl -X DELETE "https://api.salesforce.com/einstein/ai-agent/v1/sessions/YOUR_SESSION_ID" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "x-session-end-reason: UserRequest"
Response: Returns HTTP 200 with a SessionEnded message.
Note: The x-session-end-reason header is required. Valid values: UserRequest, Transfer.
Streaming Responses
For real-time streaming responses, add streaming capabilities to your session:
{
"featureSupport": "Streaming",
"streamingCapabilities": {
"chunkTypes": ["Text"]
}
}
When streaming is enabled, responses will be sent as Server-Sent Events (SSE).
Error Handling
Common error responses:
| Status Code | Description | Solution |
|---|---|---|
| 401 | Unauthorized | OAuth token expired - get a new one |
| 404 | Not Found | Check agent ID or session ID |
| 429 | Too Many Requests | Rate limit exceeded - slow down requests |
| 500 | Internal Server Error | Salesforce server issue - retry with backoff |
Rate Limits
- Sessions per hour: 1000 per org
- Messages per session: 100
- Concurrent sessions: 50 per org
SDK Examples
const axios = require("axios");
const client = new AgentAPIClient(
"https://myorg.my.salesforce.com",
"YOUR_CLIENT_ID",
"YOUR_CLIENT_SECRET"
);
class AgentAPIClient {
constructor(instanceUrl, clientId, clientSecret) {
this.instanceUrl = instanceUrl;
this.clientId = clientId;
this.clientSecret = clientSecret;
this.baseUrl = "https://api.salesforce.com/einstein/ai-agent/v1";
}
async getAccessToken() {
const response = await axios.post(
`${this.instanceUrl}/services/oauth2/token`,
new URLSearchParams({
grant_type: "client_credentials",
client_id: this.clientId,
client_secret: this.clientSecret,
}),
{ headers: { "Content-Type": "application/x-www-form-urlencoded" } }
);
return response.data.access_token;
}
async startSession(agentId, accessToken) {
const response = await axios.post(
`${this.baseUrl}/agents/${agentId}/sessions`,
{
externalSessionKey: `session_${Date.now()}`,
instanceConfig: { endpoint: this.instanceUrl },
tz: "America/Los_Angeles",
bypassUser: true,
},
{ headers: { Authorization: `Bearer ${accessToken}` } }
);
return response.data.sessionId;
}
async sendMessage(sessionId, text, sequenceId, accessToken) {
const response = await axios.post(
`${this.baseUrl}/sessions/${sessionId}/messages`,
{
message: {
sequenceId,
type: "Text",
text,
},
},
{ headers: { Authorization: `Bearer ${accessToken}` } }
);
return response.data;
}
async endSession(sessionId, accessToken) {
await axios.delete(`${this.baseUrl}/sessions/${sessionId}`, {
headers: {
Authorization: `Bearer ${accessToken}`,
"x-session-end-reason": "UserRequest",
},
});
}
}
// Usage
(async () => {
const token = await client.getAccessToken();
const sessionId = await client.startSession("YOUR_AGENT_ID", token);
const response = await client.sendMessage(sessionId, "Hello!", 1, token);
console.log(response);
await client.endSession(sessionId, token);
})();
Tips
- Token expiration: OAuth tokens expire after ~2 hours - implement token refresh logic
- Session management: Always end sessions when done to avoid hitting Salesforce limits
- Sequence tracking: Maintain sequence IDs on your side - they must increment sequentially
- Error handling: Implement retry logic with exponential backoff for transient errors
- Rate limiting: Respect rate limits and implement proper throttling