# developer Einstein Bots API and SDK (bot-api) - Full Documentation # Channel Connector Framework The channel connector framework uses Spring Boot to connect an external channel, such as Twitter or Slack, to the Salesforce Einstein Bot. The framework uses the [Einstein Bots SDK](https://developer.salesforce.com/docs/service/einstein-bot-api/sdk/sdk-overview.md) and sets up the dependencies like caching, authentication, and metrics using a Spring auto-configuration. **note:** For SDK and channel connector framework updates, see the [API Release Notes](/docs/service/einstein-bot-api/references/about/api-release-notes.html). The channel connector framework provides these features. - Auto-configuration - : The Spring beans are auto-configured using the `application.properties`. - Authentication mechanism - : If all OAuth properties ( `sfdc.einstein.bots.oauth.*` ) are configured in the application properties, the `AuthMechanism` object of the Einstein Bots SDK is configured to use the JWT Bearer OAuth flow. - Caching mechanism - : If `sfdc.einstein.bots.cache.redis-url` is included in the application properties, the SDK uses Redis cache, otherwise, the in-memory cache is implemented. - Basic and session-managed bot clients - : If you configure all required properties in `application.properties`, both `BasicChatbotClient` and `SessionManagedChatbotClient` are available with default configurations. - Bot health indicator - : The Spring’s health indicator `BotsHealthIndicator` is auto-configured to check the bot’s runtime health status. - Custom implementation support - : You can provide your own implementation of Spring beans instead of using the default one. - Metrics - : The framework uses the Spring Micrometer library to collect the metrics. You can publish the metrics to all popular monitoring systems, such as New Relic, Prometheus, and Graphite, using Spring Micrometer. # Einstein Bots API Use the Einstein Bots API to link your external channels, such as Slack or Twitter, with your Einstein Bots. Start sessions, send messages to the bot in active sessions, receive messages from the bot, and end sessions using the API. Let’s [Get Started](https://developer.salesforce.com/docs/service/einstein-bot-api/guide/get-started-overview.md) with the REST API. **important:** To learn about the Agentforce Agent API, see the [Agent API Developer Guide](https://developer.salesforce.com/docs/einstein/genai/guide/agent-api.html). **See Also** - [Introducing the Einstein Bots Platform API](https://developer.salesforce.com/blogs/2022/05/introducing-the-einstein-bots-platform-api) Developer's Blog - [API Release Notes](/docs/service/einstein-bot-api/references/about/api-release-notes.html) - [API Reference v5](/docs/service/einstein-bot-api/references/bot-api-v5?meta=Summary) - [API Reference v4](/docs/service/einstein-bot-api/references/bot-api-v4/v4.0.0?meta=Summary) Einstein Bots API # Einstein Bots API **Version:** v5.1.0 ## Servers * `https://runtime-api-na-west.prod.chatbots.sfdc.sh` Einstein Bots API - NA West * `https://runtime-api-na-east.prod.chatbots.sfdc.sh` Einstein Bots API - NA East * `https://runtime-api-eu-west.prod.chatbots.sfdc.sh` Einstein Bots API - EU West * `https://runtime-api-eu-east.prod.chatbots.sfdc.sh` Einstein Bots API - EU East * `https://runtime-api-ap-west.prod.chatbots.sfdc.sh` Einstein Bots API - AP West * `https://runtime-api-ap-east.prod.chatbots.sfdc.sh` Einstein Bots API - AP East ## Security * `chatbotAuth` (oauth2) chatbotAuth supports authorization code and implicit Oauth flows. * OAuth flow authorizationCode (authorizationUrl: https://login.salesforce.com/services/oauth2/authorize, tokenUrl: https://login.salesforce.com/services/oauth2/token) * Scope chatbot\_api: Access bot APIs * OAuth flow implicit (authorizationUrl: https://login.salesforce.com/services/oauth2/authorize) * Scope chatbot\_api: Access bot APIs * `jwtBearer` (http) Salesforce OAuth access token obtained using the JWT Bearer flow. Use the `chatbot_api` scope with this flow to enable access to the Einstein Bots services. * HTTP scheme: bearer ## Operations ### GET Check the health status `GET /status` Operation ID: `checkHealthStatus`. Tags: health. Returns whether the status of Einstein Bots API is up or down. #### Responses * **200** OK Media type: `application/json`. Schema: `Status`. * **default** Something went wrong Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` Media type: `application/json`. Schema: `Error`. ### GET Get API versions `GET /versions` Operation ID: `getAPIVersions`. Tags: versions. Retrieve the list of supported Einstein Bots API versions. #### Responses * **200** Supported API versions Media type: `application/json`. Schema: `SupportedVersions`. * **default** Something went wrong Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` Media type: `application/json`. Schema: `Error`. ### POST Start a session `POST /v5.1.0/bots/{bot-id}/sessions` Operation ID: `startSession`. Tags: bot. Security: chatbotAuth (chatbot\_api); jwtBearer. Send a message to the bots API to start a new session. #### Parameters * `bot-id` (path, string, required) ID of the Einstein Bot that you want to interact with. * `X-Org-Id` (header, string, required) 18-character org ID of the Salesforce org associated with the bot. Example: `00Dx0000000ALskEAG`. * `X-Request-ID` (header, string) Request ID. A UUID in string format created by you to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a`. #### Request body (required) Request payload to initiate a session. Media type: `application/json`. Schema: `InitMessageEnvelope`. #### Responses * **200** OK Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` * `X-Runtime-CRC` (string) Internal runtime CRC unique to the request. * `X-Bot-Mode` (string) Bot mode. Example: `default` Media type: `application/json`. Schema: `ResponseEnvelope`. * **400** Bad Request Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` Media type: `application/json`. Schema: `Error`. _Example_ ``` { "status": 400, "path": "/v1/00DRM00000067To/chatbots/HelloWorldBot/messages", "requestId": "19c056ab-d909-49df-b976-65e56b6ab214", "error": "BadRequestError", "message": "Bad Request", "timestamp": 1531245973799, "additionalProp1": {} } ``` * **401** Access bearer token is missing or invalid Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` Media type: `application/json`. Schema: `Error`. _Example_ ``` { "status": 401, "path": "/v1/00DRM00000067To/chatbots/HelloWorldBot/messages", "requestId": "19c056ab-d909-49df-b976-65e56b6ab214", "error": "UnauthorizedError", "message": "Access bearer token is missing or invalid", "timestamp": 1531245973799 } ``` * **403** User forbidden from accessing the resource Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` Media type: `application/json`. Schema: `Error`. _Example_ ``` { "status": 403, "path": "/v1/00DRM00000067To/chatbots/HelloWorldBot/messages", "requestId": "19c056ab-d909-49df-b976-65e56b6ab214", "error": "ForbiddenError", "message": "User forbidden from accessing the resource", "timestamp": 1531245973799, "additionalProp1": {} } ``` * **404** Resource not found Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` Media type: `application/json`. Schema: `Error`. _Example_ ``` { "status": 404, "path": "/v1/00DRM00000067To/chatbots/HelloWorldBot/messages", "requestId": "19c056ab-d909-49df-b976-65e56b6ab214", "error": "NotFoundError", "message": "Resource not found", "timestamp": 1531245973799, "additionalProp1": {} } ``` * **422** Any exception that occurred during the request execution Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` Media type: `application/json`. Schema: `Error`. _Example_ ``` { "status": 422, "path": "v4.0.0/messages", "requestId": "19c056ab-d909-49df-b976-65e56b6ab214", "error": "RequestProcessingException", "message": "Cannot determine the active version for the bot", "timestamp": 1531245973799, "additionalProp1": {} } ``` * **423** Server is busy and cannot process the request at this time Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` Media type: `application/json`. Schema: `Error`. _Example_ ``` { "status": 423, "path": "/v1/00DRM00000067To/chatbots/HelloWorldBot/messages", "requestId": "19c056ab-d909-49df-b976-65e56b6ab214", "error": "ServerBusyError", "message": "Server is busy and cannot process the request at this time", "timestamp": 1531245973799, "additionalProp1": {} } ``` * **429** Too many requests for the server to handle Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` Media type: `application/json`. Schema: `Error`. _Example_ ``` { "status": 429, "path": "/v1/00DRM00000067To/chatbots/HelloWorldBot/messages", "requestId": "19c056ab-d909-49df-b976-65e56b6ab214", "error": "TooManyRequestsError", "message": "Too many requests for the server to handle", "timestamp": 1531245973799, "additionalProp1": {} } ``` * **503** Service is unavailable possibly because Apex or Flow calls timed out Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` Media type: `application/json`. Schema: `Error`. _Example_ ``` { "status": 503, "path": "/v1/00DRM00000067To/chatbots/HelloWorldBot/messages", "requestId": "19c056ab-d909-49df-b976-65e56b6ab214", "error": "ServiceUnavailable", "message": "Service is unavailable possibly because Apex/Flow calls timed out", "timestamp": 1531245973799, "additionalProp1": {} } ``` * **default** Something went wrong Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` Media type: `application/json`. Schema: `Error`. ### POST Continue an active session `POST /v5.1.0/sessions/{session-id}/messages` Operation ID: `continueSession`. Tags: bot. Security: chatbotAuth (chatbot\_api); jwtBearer. Send a message to the bots API on an active session. #### Parameters * `session-id` (path, string, required) ID of an active session, which is the `sessionId` returned by the bot when you start a session. * `X-Org-Id` (header, string, required) 18-character org ID of the Salesforce org associated with the bot. Example: `00Dx0000000ALskEAG`. * `X-Request-ID` (header, string) Request ID. A UUID in string format created by you to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a`. * `X-Runtime-CRC` (header, string) Internal runtime CRC, which is the `X-Runtime-CRC` value from previous response. #### Request body (required) Request payload to continue the chat. Media type: `application/json`. Schema: `ChatMessageEnvelope`. #### Responses * **200** OK Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` * `X-Runtime-CRC` (string) Internal runtime CRC. Media type: `application/json`. Schema: `ChatMessageResponseEnvelope`. * **400** Bad Request Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` Media type: `application/json`. Schema: `Error`. _Example: same as POST /v5.1.0/bots/{bot-id}/sessions 400 response._ * **401** Access bearer token is missing or invalid Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` Media type: `application/json`. Schema: `Error`. _Example: same as POST /v5.1.0/bots/{bot-id}/sessions 401 response._ * **403** User forbidden from accessing the resource Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` Media type: `application/json`. Schema: `Error`. _Example: same as POST /v5.1.0/bots/{bot-id}/sessions 403 response._ * **404** Resource not found Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` Media type: `application/json`. Schema: `Error`. _Example: same as POST /v5.1.0/bots/{bot-id}/sessions 404 response._ * **422** Any exception that occurred during the request execution Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` Media type: `application/json`. Schema: `Error`. _Example: same as POST /v5.1.0/bots/{bot-id}/sessions 422 response._ * **423** Server is busy and cannot process the request at this time Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` Media type: `application/json`. Schema: `Error`. _Example: same as POST /v5.1.0/bots/{bot-id}/sessions 423 response._ * **429** Too many requests for the server to handle Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` Media type: `application/json`. Schema: `Error`. _Example: same as POST /v5.1.0/bots/{bot-id}/sessions 429 response._ * **503** Service is unavailable possibly because Apex or Flow calls timed out Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` Media type: `application/json`. Schema: `Error`. _Example: same as POST /v5.1.0/bots/{bot-id}/sessions 503 response._ * **default** Something went wrong Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` Media type: `application/json`. Schema: `Error`. ### DELETE End an active session `DELETE /v5.1.0/sessions/{session-id}` Operation ID: `endSession`. Tags: bot. Security: chatbotAuth (chatbot\_api); jwtBearer. Send a message to the bots API to end a session #### Parameters * `session-id` (path, string, required) ID of an active session, which is the `sessionId` returned by the bot when you start a session. * `X-Org-Id` (header, string, required) 18-character org ID of the Salesforce org associated with the bot. Example: `00Dx0000000ALskEAG`. * `X-Session-End-Reason` (header, EndSessionReason, required) Reason session ended. _Example_ ``` Transfer ``` * `X-Request-ID` (header, string) Request ID. A UUID in string format created by you to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a`. * `X-Runtime-CRC` (header, string) Internal runtime CRC, which is the `X-Runtime-CRC` value from previous response. #### Responses * **200** OK Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` * `X-Runtime-CRC` (string) Internal runtime CRC. Media type: `application/json`. Schema: `ChatMessageResponseEnvelope`. * **400** Bad Request Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` Media type: `application/json`. Schema: `Error`. _Example: same as POST /v5.1.0/bots/{bot-id}/sessions 400 response._ * **401** Access bearer token is missing or invalid Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` Media type: `application/json`. Schema: `Error`. _Example: same as POST /v5.1.0/bots/{bot-id}/sessions 401 response._ * **403** User forbidden from accessing the resource Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` Media type: `application/json`. Schema: `Error`. _Example: same as POST /v5.1.0/bots/{bot-id}/sessions 403 response._ * **404** Resource not found Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` Media type: `application/json`. Schema: `Error`. _Example: same as POST /v5.1.0/bots/{bot-id}/sessions 404 response._ * **422** Any exception that occurred during the request execution Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` Media type: `application/json`. Schema: `Error`. _Example: same as POST /v5.1.0/bots/{bot-id}/sessions 422 response._ * **423** Server is busy and cannot process the request at this time Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` Media type: `application/json`. Schema: `Error`. _Example: same as POST /v5.1.0/bots/{bot-id}/sessions 423 response._ * **429** Too many requests for the server to handle Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` Media type: `application/json`. Schema: `Error`. _Example: same as POST /v5.1.0/bots/{bot-id}/sessions 429 response._ * **503** Service is unavailable possibly because Apex or Flow calls timed out Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` Media type: `application/json`. Schema: `Error`. _Example: same as POST /v5.1.0/bots/{bot-id}/sessions 503 response._ * **default** Something went wrong Headers: * `X-Request-ID` (string) Request ID. A UUID in string format to help with request tracking. Example: `36a73651-a46d-4d16-9a8a-fd436ed62e1a` Media type: `application/json`. Schema: `Error`. ## Schemas ### Status * `status` (_string_, required) Health status of Einstein Bots API. Allowed values: `UP`, `DOWN`. Example: `UP`. ### Error * `status` (_integer (int32)_, required) HTTP status. * `path` (_string_, required) Request path. * `requestId` (_string_, required) Request ID. A UUID in string format to help with request tracking. * `error` (_string_, required) Error class name. * `message` (_string_, required) Exception message. * `timestamp` (_integer (int64)_, required) Unix timestamp. ### SupportedVersions * `versions` (_array of object_, required) List of supported API versions. * `versionNumber` (_string_) API Version number. * `status` (_string_) API Version status. Allowed values: `ACTIVE`, `DEPRECATED`. ### InitMessageEnvelope * `externalSessionKey` (_ExternalSessionKey_, required) * `message` (_TextInitMessage_) * `forceConfig` (_ForceConfig_, required) * `responseOptions` (_ResponseOptions_) * `tz` (_string_, nullable) Client timezone where the customer starts the chat. Example: `America/Los_Angeles`. * `variables` (_Variables_) * `referrers` (_array of Referrer_, nullable) Array of referrers of transferring the session to another bot. ### ResponseEnvelope * `sessionId` (_ResponseSessionId_, required) * `botVersion` (_BotVersion_, required) * `processedSequenceIds` (_array of integer (int64)_, required) Sequence IDs of messages that the bot processed. Except for the initial request to start the session, all requests require a `sequenceId`. Items: Example: `1`. * `messages` (_array of TextResponseMessage | ChoicesResponseMessage | EscalateResponseMessage | SessionEndedResponseMessage_, required) Messages from the bot. Minimum: 0. * `variables` (_Variables_) * `metrics` (_object_) Session metrics. * `intents` (_Intents_) * `entities` (_Entities_) * `_links` (_Links_, required) ### ChatMessageEnvelope * `message` (_ChoiceMessage | TextMessage | TransferSucceededRequestMessage | TransferFailedRequestMessage | RedirectMessage_, required) Message to the bot. One of: `ChoiceMessage`, `TextMessage`, `TransferSucceededRequestMessage`, `TransferFailedRequestMessage`, `RedirectMessage`. * `responseOptions` (_ResponseOptions_) ### ChatMessageResponseEnvelope * `botVersion` (_BotVersion_, required) * `processedSequenceIds` (_array of integer (int64)_, required) Sequence IDs of processed messages. Example: `1, 2`. * `messages` (_array of TextResponseMessage | ChoicesResponseMessage | EscalateResponseMessage | SessionEndedResponseMessage_, required) Messages from the bot. Minimum: 0. * `variables` (_Variables_) * `metrics` (_object_) Session metrics. * `intents` (_Intents_) * `entities` (_Entities_) * `_links` (_Links_, required) ### EndSessionReason Type: `string`. Allowed values: `UserRequest`, `Transfer`, `Expiration`, `Error`, `Other`. Example: `Transfer`. ### ExternalSessionKey UUID in string format for the conversation. You can use this parameter to trace the conversation in your bot’s event logs. Type: `string`. Example: `57904eb6-5352-4c5e-adf6-5f100572cf5d`. ### TextInitMessage * `text` (_string_) Initial message from the customer to the bot to begin a session. Example: `Hello`. ### ForceConfig API configuration parameters. * `endpoint` (_string_, required) Instance URL of your Salesforce org. You can find the value in the **ForceConfig Endpoint** field of the **Add Connection** dialog when you add the connected app to the bot. See [Get Started with Einstein Bot API](https://developer.salesforce.com/docs/service/einstein-bot-api/guide/prerequisites.html). Example: `https://d5e000009s7bceah-dev-ed.my.salesforce.com/`. ### ResponseOptions Configuration of additional information returned in the response payload. * `variables` (_ResponseOptionsVariables_) * `metrics` (_boolean_) Indicates whether to include metrics in the response. * `intents` (_boolean_) Indicates whether to include all intents detected when processing the current request. * `entities` (_boolean_) Indicates whether to include all entities detected when processing the current request. ### Variables Array of custom and context variables passed to the bot when initiating a session. See [What’s a Variable?](https://help.salesforce.com/s/articleView?id=service.bots_service_whats_a_variable.htm&type=5&_ga=2.50520745.1281711304.1657558186-1967832163.1652719780). Array of `BooleanVariable | DateVariable | DateTimeVariable | MoneyVariable | NumberVariable | TextVariable | ObjectVariable | RefVariable | ListVariable`. ### Referrer * `type` (_string_, required) Referrer type. Allowed values: `Salesforce:Core:Bot:Id`, `Salesforce:BotRuntime:Session:Id`. Example: `Salesforce:Core:Bot:Id`. * `value` (_string_, required) ID of referrer. Example: `string`. ### ResponseSessionId Bot runtime session ID. Type: `string`. Example: `57904eb6-5352-4c5e-adf6-5f100572cf5d`. ### BotVersion Bot configuration version. Type: `string`. Example: `0X9RM0000004CD00AM`. ### TextResponseMessage * `type` (_string_, required) Message type of a text message to the customer. Allowed values: `text`. Example: `text`. * `id` (_MessageId_, required) * `text` (_string_, required) Message from the bot. Example: `Hello world!`. * `schedule` (_Schedule_) ### ChoicesResponseMessage * `type` (_string_, required) Message type of a question with choices. Allowed values: `choices`. Example: `choices`. * `id` (_MessageId_, required) * `choices` (_array of object_, required) Array of answers to choose from. Each item: Choice Minimum: 1. * `label` (_string_, required) Choice label. Example: `Order Status`. * `alias` (_string_, nullable) Choice alias, for example, a number to represent the choice. Example: `1`. * `id` (_string_, required) Choice ID. Example: `8a9a745f-0c09-4b13-955c-1ab9e06c7ad7`. * `widget` (_string_, required) "Widget type of how answers appear in the client. * `buttons`—Answers appear in clickable pill boxes. * `menu`—Answers appear in clickable boxes vertically stacked." Allowed values: `buttons`, `menu`. Example: `buttons`. * `schedule` (_Schedule_) ### EscalateResponseMessage * `type` (_string_, required) Message type of an escalation message. Allowed values: `escalate`. Example: `escalate`. * `id` (_MessageId_, required) * `schedule` (_Schedule_) * `targets` (_array of object_, required) Array of transfer targets. Minimum: 0. * `type` (_string_, required) Type of the transfer target. Allowed values: `Salesforce:Core:Bot:Id`, `Salesforce:Core:Queue:Id`, `Salesforce:Core:Skill:Id`, `Salesforce:Core:Flow:Id`. Example: `Salesforce:Core:Bot:Id`. * `value` (_string_, required) ID of the transfer target. ### SessionEndedResponseMessage * `type` (_string_, required) Message type informing that the session ended. Allowed values: `sessionEnded`. Example: `sessionEnded`. * `id` (_MessageId_, required) * `reason` (_string_, required) Reason the session ended. Allowed values: `ClientRequest`, `TransferFailedNotConfigured`, `Action`, `Error`, `InfiniteLoopDetected`. Example: `ClientRequest`. * `schedule` (_Schedule_) ### Intents Array of the intents detected by the bot when processing the request. For a bot, "intent" refers to the goal the customer. See [Use Intents to Understand Your Customers](https://help.salesforce.com/s/articleView?id=service.bots_service_train_bot.htm&type=5&_ga=2.5901875.1281711304.1657558186-1967832163.1652719780). Array of `NormalizedIntent`. Minimum: 0. ### Entities Array of all entities detected by the bot when processing this request. For a bot, "Entities" refer to the modifiers the customer uses to describe their issue and intent is what they really mean. Array of `NormalizedEntity`. Minimum: 0. ### Links List of Einstein Bots API endpoints for HATEOS compliancy. * `self` (_HyperLink_, required) * `messages` (_HyperLink_) * `session` (_HyperLink_) ### ChoiceMessage * `type` (_string_, required) Message type for answering a question with choices from the bot. Allowed values: `choice`. Example: `choice`. * `sequenceId` (_SequenceId_, required) * `inReplyToMessageId` (_InReplyToMessageId_) * `choiceIndex` (_integer_, nullable) Index or alias of the chosen answer. The index starts with 1. Either `choiceIndex` or `choiceId` is required. Example: `1`. * `choiceId` (_string_, nullable) ID of the answer when you answer by clicking a choice button. Either `choiceIndex` or `choiceId` is required. Example: `8a9a745f-0c09-4b13-955c-1ab9e06c7ad7`. ### TextMessage * `type` (_string_, required) Message type for replying to the bot through text. Allowed values: `text`. Example: `text`. * `sequenceId` (_SequenceId_, required) * `inReplyToMessageId` (_InReplyToMessageId_) * `text` (_string_, required) Text reply to the bot. To answer a question with choices with a text message, type the `choiceLabel`. ### TransferSucceededRequestMessage * `type` (_string_, required) Message type for the client to inform the bot that the client successfully transferred the session to another target. Allowed values: `transferSucceeded`. Example: `transferSucceeded`. * `sequenceId` (_SequenceId_, required) * `inReplyToMessageId` (_InReplyToMessageId_) ### TransferFailedRequestMessage * `type` (_string_, required) Message type for the client to inform the bot that the client failed to transfer the session to another target. Allowed values: `transferFailed`. Example: `transferFailed`. * `sequenceId` (_SequenceId_, required) * `inReplyToMessageId` (_InReplyToMessageId_) * `reason` (_string_, required) Reason for the failed transfer. Allowed values: `NoAgentAvailable`, `Error`. Example: `NoAgentAvailable`. * `description` (_string_, nullable) Description of why the transfer failed. ### RedirectMessage * `type` (_string_, required) Message type for the redirecting the client to another bot flow. Allowed values: `redirect`. Example: `redirect`. * `sequenceId` (_SequenceId_, required) * `dialogId` (_string_, required) Dialog ID to redirect to. Example: `68f934fb-e022-37a7-612e-b74fc87191d9`. ### ResponseOptionsVariables Variables returned in the response. * `include` (_boolean_, required) Indicates whether to include variables in the response. Example: `true`. * `filter` (_array of string_, nullable) Filter the returned variables. If missing, null, or empty, no filtering is applied. Each item: Variable name to filter the returned variables on. Items: Example: `OrderQty`. _Example_ ``` [ "OrderQty", "OrderType" ] ``` * `onlyChanged` (_boolean_, required) Indicates whether the response contains only changed variables. Example: `true`. ### BooleanVariable * `name` (_string_, required) API name of a variable defined in the bot definition. Example: `isShipped`. * `type` (_string_, required) Variable type of a boolean variable. Allowed values: `boolean`. Example: `boolean`. * `value` (_boolean_, required, nullable) Boolean value. Example: `true`. ### DateVariable * `name` (_string_, required) API name of a variable defined in the bot definition. Example: `orderDate`. * `type` (_string_, required) Variable type of a date variable. Allowed values: `date`. Example: `date`. * `value` (_string_, required, nullable) Date in format ISO\_LOCAL\_DATE 'YYYY-MM-DD'. Example: `2021-09-21`. ### DateTimeVariable * `name` (_string_, required) API name of a variable defined in the bot definition. Example: `orderDateTime`. * `type` (_string_, required) Variable type of a datetime variable. Allowed values: `dateTime`. Example: `dateTime`. * `value` (_string_, required, nullable) Datetime in format ISO\_LOCAL\_DATE\_TIME 'YYYY-MM-DDTHH:MM:SS'. Example: `2018-09-21T14:30:00`. ### MoneyVariable * `name` (_string_, required) API name of a variable defined in the bot definition. Example: `orderAmount`. * `type` (_string_, required) Variable type of a money variable storing the currency and amount. Allowed values: `money`. Example: `money`. * `value` (_string_, required, nullable) Money in format '3\_LETTER\_CURRENCY\_CODE AMOUNT'. Example: `USD 10.40`. ### NumberVariable * `name` (_string_, required) API name of a variable defined in the bot definition. Example: `qty`. * `type` (_string_, required) Variable type of a number variable. Allowed values: `number`. Example: `number`. * `value` (_number_, required, nullable) Variable value in number format. Example: `10`. ### TextVariable * `name` (_string_, required) API name of a variable defined in the bot definition. Example: `note`. * `type` (_string_, required) Variable type of a text variable. Allowed values: `text`. Example: `text`. * `value` (_string_, required, nullable) Variable value in text format. Example: `Thanks for your order!`. ### ObjectVariable * `name` (_string_, required) API name of a variable defined in the bot definition. Example: `contact`. * `type` (_string_, required) Variable type of an object variable. Allowed values: `object`. Example: `object`. * `value` (_object_, required, nullable) Variable value is an object. Includes all fields of `Variables`. _Example_ ``` [ { "name": "fullName", "type": "text", "value": "Matt Smith" } ] ``` ### RefVariable * `name` (_string_, required) API name of a variable defined in the bot definition. Example: `target`. * `type` (_string_, required) Variable type of a reference ID variable. Allowed values: `ref`. Example: `ref`. * `value` (_string_, required, nullable) Variable value in reference ID format. Example: `1M5xx000000000BCAQ`. ### ListVariable * `name` (_string_, required) API name of a variable defined in the bot definition. Example: `target`. * `type` (_string_, required) Object variable that contains a list. Allowed values: `list`. Example: `list`. * `value` (_array of object_, required, nullable) Array of objects. _Example_ ``` [ { "type": "ref", "value": "1M5xx000000000BCAQ" } ] ``` ### MessageId UUID that references this message. Type: `string`. Example: `a133c185-73a7-4adf-b6d9-b7fd62babb4e`. ### Schedule * `responseDelayMilliseconds` (_integer (int32)_, required) Delay in ms to display the bot’s message to the user. This parameter is set by the bot admin when creating the bot. Example: `1200`. ### NormalizedIntent * `label` (_string_, required) Name or ID of the intent. Example: `Password help`. * `confidenceScore` (_number_, required) Probability number that the AI assigns to this intent. Example: `0.78`. * `intentSource` (_IntentSource_, required) ### NormalizedEntity * `confidenceScore` (_number_, required) Probability number that the AI assigns to this entity. Example: `0.4`. * `value` (_string_, required) Value of the entity that the customer enters. Example: `USD 30.0`. * `type` (_EntityType_, required) * `entitySource` (_EntitySource_) ### HyperLink Hyperlink object included in the Links schema * `href` (_string_) Link to the endpoint Example: `https://runtime-api-ap-east.prod.chatbots.sfdc.sh/v5.0.0/sessions`. ### SequenceId Client-generated sequence number of the message in a session. Increase this number for each subsequent message. Type: `integer (int64)`. Example: `1`. ### InReplyToMessageId Message ID of the previous response you are replying to. Type: `string`. Example: `a133c185-73a7-4adf-b6d9-b7fd62babb4e`. ### IntentSource Prediction system that detects the intent. * `DIRECT`—Utterance search or an exact match * `CORE_NLP`—Third-party Natural Language Processing (NLP) * `EINSTEIN_NLP`—Einstein NLP Type: `string`. Allowed values: `DIRECT`, `CORE_NLP`, `EINSTEIN_NLP`. Example: `EINSTEIN_NLP`. ### EntityType System or custom entity Type ([What’s an Entity?](https://help.salesforce.com/s/articleView?id=service.bots_service_whats_an_entity.htm&type=5)). Type: `string`. Example: `MONEY`. ### EntitySource "Prediction system that detects the entity. The Bot API can call NLPs or it can figure out the entity by itself." * `BOT_SERVICE`—Bot detects entity without Natural Language Processing (NLP) * `CORE_NLP`—Third-party NLP * `EINSTEIN_NLP`—Einstein NLP Type: `string`. Allowed values: `BOT_SERVICE`, `CORE_NLP`, `EINSTEIN_NLP`. Example: `EINSTEIN_NLP`. ### EndSessionMessage Client request to end the session. * `type` (_string_, required) Message type for the client requesting the session to end. Allowed values: `endSession`. Example: `endSession`. * `sequenceId` (_SequenceId_, required) * `inReplyToMessageId` (_InReplyToMessageId_) * `reason` (_EndSessionReason_, required) # Einstein Bots API Release Notes The API Release Notes include updates to the Einstein Bots REST API, SDK, and channel connector framework. To see the latest feature updates, visit [Einstein Bots Release Notes](https://help.salesforce.com/s/articleView?id=release-notes.rn_einstein_bots.htm&type=5). ## 02/28/2023 ### API Release v5.1.0 - Removed support for sending file attachments. ### SDK v2.1.X - Uses API v5.1.0 and therefore doesn't support sending file attachments. ## 05/31/2022 ### API Release v5.0.0 - Connect your channels faster with v5.0.0, which is more RESTful and simpler to use. - Three new endpoints handle sessions instead of one. - Start a new session - Continue an existing session - End an existing session - Bot and chat session IDs are path parameters instead of request body parameters. - Supports bot responses with rich file attachments. - Includes intents and entities in the API responses. - API version 5 is HATEOS-compliant. ### SDK and Channel Connector Framework Release v2.0.X - First publicly available version of the SDK and connector framework. - Einstein Bots Java SDK integrates your client with the bot’s REST API v5.0.0 using Java-based tools and libraries. - The channel connector framework uses Spring Boot to connect your app to the SDK. - A version check makes sure you’re running the latest SDK version. You always need to be running the latest SDK version. ## 01/31/2022 ### API Release v4.0.0 - First publicly available version of the Einstein Bots API. # Einstein Bots SDK Use [Einstein Bots API](https://developer.salesforce.com/docs/service/einstein-bot-api/api/api-overview.md) (Beta) to link your external messaging channels with your Einstein Bots. Use the Einstein Bots Java SDK to integrate your channel with the Einstein Bots API. This SDK guide is for API version 5. If you use [Spring Boot](https://spring.io/projects/spring-boot), you can connect your app to the SDK through the [Channel Connector Framework](https://developer.salesforce.com/docs/service/einstein-bot-api/channel-connector/connector-overview.md). If not, use the SDK directly. **note:** For SDK and channel connector framework updates, see the [API Release Notes](/docs/service/einstein-bot-api/references/about/api-release-notes.html). The Java SDK simplifies integrating your service client with the bot’s REST API. Here are the features that the SDK provides. - Authentication mechanism - : Einstein Bots API requires the client to authenticate requests using OAuth. The SDK uses the [JWT Bearer OAuth Flow](https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_oauth_jwt_flow.htm&type=5) as a default, but any OAuth flow can be used to get the access token. See [AuthMechanism interface](https://github.com/forcedotcom/einstein-bot-sdk-java/blob/master/src/main/java/com/salesforce/einsteinbot/sdk/auth/AuthMechanism.java). - Session management - : You only need the external session ID, such as a Twitter thread ID, to track the bot session. The client maps the external session ID to the internal session ID in the cache. The Einstein Bots API has separate calls to start a session and send subsequent messages. The session-managed client simplifies the calls by having one method to start and continue a session. - Caching mechanism - : The SDK uses a cache to store the mapping between the `ExternalSessionId` and internal `RuntimeSessionId`. We support the Redis cache manager by default. You can implement additional caching methods with the [Cache interface](https://github.com/forcedotcom/einstein-bot-sdk-java/blob/master/src/main/java/com/salesforce/einsteinbot/sdk/cache/Cache.java). - DTO classes - : The SDK provides DTO classes with strict type checking to serialize and deserialize JSON request and response bodies. - DSL-style methods - : The SDK provides DSL-style methods so that you can write more readable code. # End a Bot Session If the bot has no more actions to take, the customer can end the session. ```java // Build the request to end the bot session. BotEndSessionRequest botEndSessionRequest = BotRequest .withEndSession(EndSessionReason.USERREQUEST).build(); // Retrieve the RuntimeSessionId from the sessionId returned by the bot when // the session starts. RuntimeSessionId runtimeSessionId = new RuntimeSessionId(sessionId); // Send the request to the bot to end the session. BotResponse endSessionResponse = client .endChatSession(config, runtimeSessionId, botEndSessionRequest); ``` Here’s an example of a text output from the bot’s response when the session ends. ```bash // Response output The chat session ended with reason: ClientRequest ``` # Error Handling The Einstein Bots API can return the following error responses. ## 4XX Responses - `BadRequestError`: Sent anytime a request contains an unexpected parameter (for example, a path parameter or a payload parameter). - `UnauthorizedError`: Sent anytime there’s a generic auth issue with the request. Fix the auth issue and resend the request. - `ForbiddenError`: Sent anytime there’s an authorization issue with the request (that is, the request has unexpected scopes). Ensure the corresponding OAuth connected app has the proper scopes and resend the request. - `NotFoundError`: Sent anytime there’s a generic resource issue, such as when the wrong bot ID, org ID, or session was provided in the request. - `NotAvailableError`: Sent anytime the internal session ID sent in the request payload is invalid. - `RichContentCapabilitiesMissingException`: Sent when the bot tries to send a File element and the client didn’t set `richContentCapabilities` in the initial request to begin the session. Available in v5.0 only. - `ServerBusyError`: Sent anytime the server is already processing a request for a given session ID. Retry the request after a response is received for any ongoing request or after 30 seconds. - `TooManyRequestsError`: Sent when the number of requests for an org or session exceeds the limit. ## 5XX Responses - `ServiceUnavailableError`: Sent anytime the server is executing a request and runs into an error or exception condition. # Error Handling If the bot API returns an error, the SDK wraps the error into a `ChatbotResponseException` object. This example shows how to retrieve the error details using `try` and `catch`. ```java // Build the request to end the session. BotEndSessionRequest botEndSessionRequest = BotRequest .withEndSession(EndSessionReason.USERREQUEST).build(); // Create an invalid session ID to return an error. RuntimeSessionId invalidSessionId = new RuntimeSessionId("invalid session id"); // Send the request to end the session with error handling. try{ BotResponse endSessionResponse = client .endChatSession(config, invalidSessionId , botEndSessionRequest); }catch (Exception e){ // The error is wrapped in RuntimeException and ExecutionException. // Therefore, call getCause() twice to find the root cause. ChatbotResponseException chatbotResponseException = ((ChatbotResponseException)e.getCause().getCause()); int httpStatusCode = chatbotResponseException.getStatus(); System.out.println("Error Http Status Code : " + httpStatusCode); Error errorResponse = chatbotResponseException.getErrorResponse(); System.out.println("Error Payload : " + errorResponse); } ``` # Explore Example Channel Connector Apps You can find examples of apps that use the channel connector framework in the [einstein-bot-channel-connector](https://github.com/forcedotcom/einstein-bot-channel-connector/tree/master/examples) Github repo. The repo conatins examples of: - MS Teams connector - Slack connector - Twilio SMS connector - Twitter connector - UI connector **note:** The example channel connector apps are not further maintained or supported by Salesforce. # Get Started Learn how to get started with the Einstein Bot channel connector framework to send messages between your external messaging channel, such as Twitter or Slack, and the Salesforce bot. ## Create a Channel Connector App You can create an app that uses the channel connector framework with this Maven archetype command. Set `archetypeVersion` to the most recently released version of the [Einstein Bot Channel Connector Archetype](https://mvnrepository.com/artifact/com.salesforce.einsteinbot/einstein-bot-channel-connector-archetype). Set `package`, `groupId`, and `artifactId` to the names of your choice. ```java mvn archetype:generate \ -DarchetypeGroupId=com.salesforce.einsteinbot \ -DarchetypeArtifactId=einstein-bot-channel-connector-archetype \ -DarchetypeVersion=1.0.1 -Dpackage=com.mycompany.myapp.channel \ -DgroupId=com.mycompany.myapp -DartifactId=myapp-channel-connector \ -Dversion=1.0.0-SNAPSHOT ``` Alternatively, you can use this command to run it in interactive mode and provide the `groupId` and `artifactId` when prompted. ```java mvn archetype:generate \ -DarchetypeGroupId=com.salesforce.einsteinbot \ -DarchetypeArtifactId=einstein-bot-channel-connector-archetype \ -DarchetypeVersion=2.0.1 ``` ## Run the Channel Connector App First, configure the `src/main/resources/application.properties` file. You can start by copying the example properties from the `src/main/resources/application.example.properties` file. This table lists the available application properties. | Property Name | Environment Variable Name | Description | | ----------------------------------------------- | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `sfdc.einstein.bots.runtime-url` | `BOT_RUNTIME_URL` | Required. The base URL of the bot runtime. | | `sfdc.einstein.bots.integration-name` | `BOT_INTEGRATION_NAME` | The integration name that you used when connecting the connected app API to the bot. See [Get Started with Einstein Bots API](https://developer.salesforce.com/docs/service/einstein-bot-api/prerequisites.md). | | `sfdc.einstein.bots.oauth.private-key-file` | `OAUTH_PRIVATE_KEY_FILE_PATH` | Required. The path to the private key in DER encoded format to get OAuth tokens. | | `sfdc.einstein.bots.oauth.login-endpoint` | `LOGIN_END_POINT` | Required. The Salesforce URL to get the OAuth Token. This is `https://login.salesforce.com` for the production org and `https://test.salesforce.com` for the test org. | | `sfdc.einstein.bots.oauth.connected-app-id` | `CONNECTED_APP_ID` | Required. The consumer key of your [connector app setup](https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_terminology.htm&type=5). | | `sfdc.einstein.bots.oauth.connected-app-secret` | `CONNECTED_APP_SECRET` | Required. The consumer secret of your [connector app setup](https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_terminology.htm&type=5). | | `sfdc.einstein.bots.oauth.user-id` | `SFDC_USER_ID` | Required. The login user ID that you use to log in to Salesforce. | | `sfdc.einstein.bots.cache.ttlseconds` | `CACHE_TTL_SECS` | The time in seconds to cache information for tracking sessions. Provide a value appropriate to your external channel. | | `sfdc.einstein.bots.cache.redis-url` | `CACHE_REDIS_URL` | The Redis URL. Required to use the Redis cache. | | `sfdc.einstein.bots.oauth.cache.ttlseconds` | `OAUTH_CACHE_TTL_SECS` | The time in seconds to cache oauth tokens to avoid network requests. | | `sfdc.einstein.bots.oauth.cache.redis-url` | `OAUTH_CACHE_REDIS_URL` | The Redis URL. Required to use Redis for caching OAuth tokens. | Start the app by running Spring Boot with this Maven command. ```bash mvn spring-boot:run ``` ## Use the Default Spring Bean Use `@Autowired` or `@Inject` in your Spring-managed code to use Bean with the default configuration. ```java @Autowired private BasicChatbotClient basicChatbotClient; @Autowired private SessionManagedChatbotClient sessionManagedChatbotClient; @Autowired private BotsHealthIndicator health; ``` Now, let’s [Explore Example Channel Connector Apps](https://developer.salesforce.com/docs/service/einstein-bot-api/guide/connector-explore-examples.md) to build your connector app. # Get Started Learn how to get started with the Einstein Bots SDK to send messages between your external messaging channel, such as Twitter or Slack, and the Salesforce bot. # Get Started with Einstein Bots API To access Einstein Bots APIs over a public network, you must set up an OAuth connected app in the org that authorizes the service calls to Einstein Bots APIs. To get started, create a connected app in Salesforce and connect an Einstein bot to the app. **important:** To learn about the Agentforce Agent API, see the [Agent API Developer Guide](https://developer.salesforce.com/docs/einstein/genai/guide/agent-api.html). ## Step 1: Create a Connected App A connected app is a framework that allows external services to integrate with Salesforce APIs and services through well-known authorization protocols such as SAML, OAuth, and OIDC. A connected app authenticates and authorizes an inbound external service call. This connection allows the external services to talk to Salesforce APIs over a public network. To learn more about Salesforce Connected Apps, refer to [Connected Apps](https://help.salesforce.com/s/articleView?id=xcloud.connected_app_overview.htm&type=5) in Salesforce Help. 1. Create a connected app following [Configure Basic Connected App Settings](https://help.salesforce.com/s/articleView?id=xcloud.connected_app_create_basics.htm&type=5) and [Enable OAuth Setting for API Integration](https://help.salesforce.com/s/articleView?id=xcloud.connected_app_create_api_integration.htm&type=5&language=en_US) with these OAuth scopes. - **Access bot services (**`chatbot_api`**)**: Required. This scope enables access to the Einstein Bots services. - **Perform requests at any time (**`refresh_token`, `offline_access`**)**: Required. This scope permits you to get an OAuth access token. 2. Choose an [OAuth Authorization Flow](https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_oauth_flows.htm&type=5). We recommend the [JWT Bearer Flow](https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_oauth_jwt_flow.htm&type=5). To use it, make sure to configure JWT bearer flow settings in the connected app. [Username-password flow](https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_oauth_username_password_flow.htm&type=5) isn’t supported. 3. Go to **Manage Connected Apps** in **Setup** in your org and set the **Permitted Users Policy** to **Admin-approved users are pre-authorized**. This policy setting ensures that all valid scopes are included when the access token is issued. 4. Retrieve the access token with the auth flow of your choice. You need a valid OAuth access token with proper scopes to access the Einstein Bots API. ## Step 2: Configure an Einstein Bot Next, connect your bot to the OAuth-enabled connected app. 1. Create and set up an Einstein Bot using the [Einstein Bots setup instructions](https://help.salesforce.com/s/articleView?id=service.bots_service_create_scratch.htm&type=5). Linking your bot to an existing deployment is optional. 2. In the Connections section of the [Update the Bot Overview Page](https://help.salesforce.com/s/articleView?id=service.bots_service_new_bot.htm&type=5) click **Add** and fill in the fields. 1. **Connection**: Select **API**. 2. **Integration Name**: **A**dd a unique name for this bot API and connected app integration, for example, “Einstein Bots Open API”. A maximum of 128 characters is allowed. 3. **Connected App**: Select the name of the OAuth-enabled connected app that you created. ![Add Connection](https://developer.salesforce.com/docs/media/bots_connection_ForceConfig.png) 3. Keep a copy of the runtime base URL and the ForceConfig endpoint. The runtime base URL is the host URL of the Einstein Bots API endpoint. You need those parameters to begin a session using the API, SDK, or connector framework. **note:** You can find the runtime base URL only here. You can find the ForceConfig endpoint under **Setup | My Domain** too. 4. Click **Save**. 5. Click **Activate** to activate your bot. You’ve now established the connection between your Einstein Bot and a Salesforce OAuth-enabled connected app. **See Also** - [Introducing the Einstein Bots Platform API](https://developer.salesforce.com/blogs/2022/05/introducing-the-einstein-bots-platform-api) Developer's Blog # Get Started with the Einstein Bots API Learn how to send and receive message from your Einstein Bot using the Einstein Bots API. - [Get Started with Version 5](https://developer.salesforce.com/docs/service/einstein-bot-api/guide/v5/get-started-v5.md) - [Get Started with Version 4](https://developer.salesforce.com/docs/service/einstein-bot-api/guide/v4/get-started-v4.md) **important:** To learn about the Agentforce Agent API, see the [Agent API Developer Guide](https://developer.salesforce.com/docs/einstein/genai/guide/agent-api.html). # Get the Health Status Both the `BasicChatbotClient` and `SessionManagedChatbotClient` interfaces provide the `getHealthStatus()` method to retrieve the bot’s health status. ```java Status status = client.getHealthStatus(); boolean isRuntimeUp = status.equals(StatusEnum.UP); ``` # Pass Custom Context Variables to the Bot With `variables` you can optionally pass custom context variables to the bot when initiating a bot session. See [What’s a Variable?](https://help.salesforce.com/s/articleView?id=service.bots_service_whats_a_variable.htm&type=5). Let’s say you want the bot to greet the customer with their name. First, configure a `CustomerName` context variable in the Bot Builder. ![Pass custom context variables to the bot](https://developer.salesforce.com/media/pass-custom-context-variables.png) Then create a `variables` list and add the context variable with the value. Include this in the initial `BotSendMessageRequest()` object when you start the session. ```java List variables = Collections .singletonList(new TextVariable() .name("CustomerName") // Context variableName .type(TypeEnum.TEXT) .value("Jane Doe")// Context variableValue ); BotSendMessageRequest botSendInitMessageRequest = BotRequest .withMessage(message) .variables(variables) .requestId(UUID.randomUUID().toString()) .build(); ``` You can then configure the greeting message to show the customer’s name. Here is the bot’s response message using the `CustomerName` context variable with the value “Jane Doe”. ```bash Hi Jane Doe, I’m a demo bot for the user guide. Choose one of the options below 1.Order Status 2.Frequently Asked Questions 3.Transfer To Agent 4.End Chat ``` # Retrieve the Bot’s Response The `BotResponse` object provides methods to retrieve the HTTP status code, response HTTP header parameters and response envelope from the bot’s message. ```java // Retrieve the HTTP status code. int httpStatusCode = response.getHttpStatusCode(); // Retrieve the response HTTP header parameters. BotHttpHeaders httpHeaders = response.getHttpHeaders(); // Retrieve all header parameters. Map> allHeaders = httpHeaders.getAll() // Retrieve a specific header parameter. Optional requestId = httpHeaders.getRequestIdHeader(); // Retrieve the response envelope. ResponseEnvelope responseEnvelope = endSessionResponse .getResponseEnvelope(); ``` Here is an example of a response body from the bot in JSON format. You can find the API response body schema in our Reference for API v5. This example `messages` array has messages with the `text` type and `choices` type. These types correspond to the TextResponseMessage and ChoicesResponseMessage schemas in the API reference. ```json { "sessionId": "32f468af-bbea-48ed-947d-95e34c05023b", "botVersion": "0X9SB00000007cb0AA", "processedSequenceIds": [1650390206434], "messages": [ { "type": "text", "id": "fde67029-c480-454d-9326-8e3024076b92", "text": "What is Refund policy?", "schedule": { "responseDelayMilliseconds": 1200 } }, { "type": "text", "id": "48ddb49d-cd4e-4f6a-b897-3e6730265f8b", "text": "Product are refundable with 30 days of purchase.", "schedule": { "responseDelayMilliseconds": 1200 } }, { "type": "text", "id": "2ef4993b-4e48-4686-806d-2b0bcf053f2b", "text": "Choose one of the options below", "schedule": { "responseDelayMilliseconds": 1200 } }, { "type": "choices", "id": "0267b1d2-d48b-4457-b364-e65e7b52a84e", "choices": [ { "label": "Order Status", "alias": "1", "id": "8acdd120-0bf7-4f4f-bece-6946dd8ece04" }, { "label": "Frequently Asked Questions", "alias": "2", "id": "95356019-f46c-4fc6-bd81-df67679b91ea" }, { "label": "Transfer To Agent", "alias": "3", "id": "3e7debac-cb3d-4154-a923-802f45b1f0b7" }, { "label": "End Chat", "alias": "4", "id": "96ad8494-b407-439e-968d-62762d902157" } ], "widget": "menu", "schedule": { "responseDelayMilliseconds": 1200 } } ], "variables": null, "_links": null } ``` The SDK automatically deserializes the JSON body into Java classes. This sample code demonstrates how to extract the bot’s message to the customer from the response body. Not all possible response body schemas and fields are included here. ```java private String getResponseMessageAsText(List messages) { StringBuilder sb = new StringBuilder(); for(AnyResponseMessage message : messages){ if (message instanceof TextResponseMessage){ sb.append(((TextResponseMessage) message).getText()) .append("\n"); }else if (message instanceof ChoicesResponseMessage){ List choices = ((ChoicesResponseMessage) message) .getChoices(); for (ChoicesResponseMessageChoices choice : choices){ sb.append(choice.getAlias()) .append(".") .append(choice.getLabel()) .append("\n"); } }else if (message instanceof EscalateResponseMessage){ List targets = ((EscalateResponseMessage) message) .getTargets(); // Add code here to transfer the bot to another target. sb.append("Transferring you to Agent in Target : " + targets); }else if (message instanceof SessionEndedResponseMessage){ ReasonEnum reason = ((SessionEndedResponseMessage) message) .getReason(); // Add code here to clean up the channel. sb.append("The Chat session ended with Reason : " + reason);str } } return sb.toString(); } System.out.println(getResponseMessageAsText(response.getResponseEnvelope() .getMessages())); ``` Here’s the extracted bot’s message to the customer in text format. ```bash Hi, I’m a demo bot for the user guide. Choose one of the options below 1.Order Status 2.Frequently Asked Questions 3.Transfer To Agent 4.End Chat ``` # Send a Message to the Bot Let’s stick with the example where the bot greets the customer with a welcome message and a choices menu. See [Retrieve the Bot’s Response](https://developer.salesforce.com/docs/service/einstein-bot-api/guide/sdk-basic-bot-client-retrieve-bots-response.md) for the JSON response body. ```bash Hi Jane Doe, I’m a demo bot for the user guide. Choose one of the options below 1.Order Status 2.Frequently Asked Questions 3.Transfer To Agent 4.End Chat ``` There are two methods that the customer can use to send an answer back to the bot. Method 1: The customer answers the bot with a choice `alias` or `label`, for example, “1” for “Order Status”. The message object of the request has the `TextMessage` type in this case. ```java // 1a. Answer with the choice alias. AnyRequestMessage message = new TextMessage() .text("1") .type(TextMessage.TypeEnum.TEXT) .sequenceId(System.currentTimeMillis()); // 1b. Answer with the choice label. AnyRequestMessage message = new TextMessage() .text("Order Status") .type(TextMessage.TypeEnum.TEXT) .sequenceId(System.currentTimeMillis()); ``` Method 2: The customer answers the bot by selecting a menu choice. The message object of the request has the `ChoiceMessage` type and a `choiceIndex()`. The index starts with 0, for example, “Order Status” has an index of 0. ```java int index=0; // 2. Answer with the choice index. AnyRequestMessage message = new ChoiceMessage() .type(ChoiceMessage.TypeEnum.CHOICE) .sequenceId(System.currentTimeMillis()) .choiceIndex(index); ``` Now, build the request with the message. ```java // Build the request with this message. BotSendMessageRequest botSendInitMessageRequest = BotRequest .withMessage(message) .build(); // Retrieve the RuntimeSessionId from the sessionId returned by the bot when // the session starts. RuntimeSessionId runtimeSessionId = new RuntimeSessionId(sessionId); // Send the request to the bot. BotResponse response = client .sendMessage(config, runtimeSessionId, botSendMessageRequest); ``` Then extract the bot’s message from the response envelope. ```java // Parse the response envelope. System.out.println(getResponseMessageAsText(response.getResponseEnvelope() .getMessages())); ``` Here is an example output to the customer stating what the order status is. ```bash // Message output You should receive your item tomorrow. ``` # Set Up the Basic Bot Client First, create an instance of the basic bot client. ```java // Create a JWT Bearer authentication mechanism. AuthMechanism oAuth = JwtBearerOAuth.with() .privateKeyFilePath(privateKeyFilePath) .loginEndpoint(loginEndpoint) .connectedAppId(connectedAppId) .connectedAppSecret(secret) .userId(userId) .build(); // Create a basic bot client. BasicChatbotClient client = ChatbotClients.basic() .basePath(basePath) .authMechanism(oAuth) .build(); ``` The `basePath` is the runtime URL that you configured in the [Set Up the Dependency and Configuration Variables](https://developer.salesforce.com/docs/service/einstein-bot-api/guide/sdk-set-up.md) section. Then create a `RequestConfig` object and add the bot ID, org ID, forceConfig endpoint configuration variables. ```java RequestConfig config = RequestConfig.with() .botId(botId) .orgId(orgId) .forceConfigEndpoint(forceConfigEndPoint) .build(); ``` # Set Up the Dependency and Config Variables To run the Einstein Bots SDK, add this POM dependency to your `pom.xml`. For ``, use the latest [Einstein Bots SDK Java version](https://mvnrepository.com/artifact/com.salesforce.einsteinbot/einstein-bot-sdk-java). ```xml com.salesforce.einsteinbot einstein-bot-sdk-java ${einstein-bot-sdk-java-version} ``` Then set up these configuration variables. ```java private final String basePath = "BOT_RUNTIME_URL"; private final String orgId = "YOUR_ORG_ID"; private final String botId = "YOUR_BOT_ID"; private final String forceConfigEndPoint = "MY_DOMAIN_LOGIN_URL"; private final String loginEndpoint = "SALESFORCE_LOGIN_END_POINT"; private final String connectedAppId = "YOUR_CONNECTED_APP_ID"; private final String secret = "YOUR_CONNECTED_APP_SECRET"; private final String userId = "YOUR_SALESFORCE_LOGIN_USER"; private final String privateKeyFilePath = "/path/to/YOUR_CONNECTED_APP_PRIVATE_KEY.der"; ``` Here’s how you find the configuration values. - `basePath`: The runtime base URL found in the **Add Connection** dialog when you add the connected app to the bot. See [Get Started with Einstein Bots API](https://developer.salesforce.com/docs/service/einstein-bot-api/prerequisites.md). - `orgId`: The 18 character org ID of the Salesforce org that is associated with the bot. To get the org ID: 1. [Find your Salesforce Organization ID](https://help.salesforce.com/s/articleView?id=000325251&type=1) (15 characters long). 2. Then convert it from the 15 to 18 character version. You can find Salesforce org ID converters using any search engine. - `botId`: The bot ID found on the [Bot Overview Page](https://help.salesforce.com/s/articleView?id=service.bots_service_new_bot.htm&type=5). - `forceConfigEndPoint`: The forceConfig endpoint found in the **Add Connection** dialog when you add the connected app to the bot. See [Get Started with Einstein Bots API](https://developer.salesforce.com/docs/service/einstein-bot-api/prerequisites.md). - `loginEndpoint`: The Salesforce login endpoint. This is `https://login.salesforce.com` for the production org and `https://test.salesforce.com` for a sandbox org. - `connectedAppId`: The consumer key of your [connector app setup](https://help.salesforce.com/s/articleView?id=xcloud.external_identity_login_step_2.htm&type=5). - `secret`: The consumer secret of [connector app setup](https://help.salesforce.com/s/articleView?id=xcloud.external_identity_login_step_2.htm&type=5). - `userId`: The user ID that you use to log in to Salesforce. - `privateKeyFilePath`: Required for the JTW bearer flow. The private key that you used to create the public certificate and uploaded to the connected app for the [OAuth 2.0 JWT Bearer Flow for Server-to-Server Integration](https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_oauth_jwt_flow.htm&type=5). If your private key is in `PEM` format, convert it to a DER format using this command. ```bash openssl pkcs8 -topk8 -inform PEM -outform DER \ -in server.key -out src/test/resources/YourPrivateKey.der \ -nocrypt. ``` NOTE: The private key is required only for the JWT bearer flow. If you aren’t using the JWT bearer flow, provide your own authorization mechanism. # Start a Session To start a session with the bot, the request body from the external channel `BotSendMessageRequest` must have the type `TextMessage`. Here the customer sends a “Hello” message to the bot. The `requestId` is an optional UUID that you can provide to help track the requests. ```java // Create the message. AnyRequestMessage message = new TextMessage() .text("Hello") .type(TextMessage.TypeEnum.TEXT) .sequenceId(System.currentTimeMillis()); // Create the request body with the message. BotSendMessageRequest botSendInitMessageRequest = BotRequest .withMessage(message) .requestId(UUID.randomUUID().toString()) .build(); ``` Then retrieve the `ExternalSessionId` from the external channel, for example, a Slack or Twitter thread ID. We’re using a random UUID for the external session ID in the sample code. ```java ExternalSessionId externalSessionKey = new ExternalSessionId(UUID.randomUUID().toString()); ``` Start the session using the `startChatSession()` method. ```java BotResponse response = client .startChatSession(config, externalSessionKey, botSendInitMessageRequest); ``` Retrieve the bot’s session ID from the `BotResponse` object. Use this session ID to continue and end the session. ```java String sessionId = response.getResponseEnvelope().getSessionId(); ``` # Transfer the Bot Session to Another Target The bot can send an escalation message to the client or an option to transfer the session to another target because of, for example, an error, or by user request. Another target can be, for example, a different bot, agent, agent queue, Salesforce skill, or Salesforce flow. Let’s stick with the example where the bot greets the customer with a welcome message and a choices menu. See [Retrieve the Bot’s Response](https://developer.salesforce.com/docs/service/einstein-bot-api/guide/sdk-basic-bot-client-retrieve-bots-response.md) for the JSON response body. ```bash Hi Jane Doe, I’m a demo bot for the user guide. Choose one of the options below 1.Order Status 2.Frequently Asked Questions 3.Transfer To Agent 4.End Chat ``` In this example, the client selects the “Transfer To Agent” option. This sample code shows how the client sends the option as a `TextMessage` type to the bot. See [Send a Message to the Bot](https://developer.salesforce.com/docs/service/einstein-bot-api/guide/sdk-basic-bot-client-send-message.md). ```java // Build the message with the selected option as a TextMessage type. AnyRequestMessage message = new TextMessage() .text("Transfer To Agent") .type(TextMessage.TypeEnum.TEXT) .sequenceId(System.currentTimeMillis()); // Build the request. BotSendMessageRequest botSendInitMessageRequest = BotRequest .withMessage(message) .build(); // Retrieve the RuntimeSessionId from the sessionId returned by the bot when // the session starts. RuntimeSessionId runtimeSessionId = new RuntimeSessionId(sessionId); // Send the request to the bot. BotResponse response = client .sendMessage(config, runtimeSessionId, botSendMessageRequest); ``` Retrieve the bot’s message from the response. ```java // Extract the message from the response envelope. System.out.println(getResponseMessageAsText(response.getResponseEnvelope() .getMessages())); ``` Here is an example text output from the bot. ```java Transferring you to agent! Transferring you to Agent in Target : [class EscalateResponseMessageTargets { value: 00GSB0000000d6E2AQ type: Salesforce:Core:Queue:Id }] ``` The API itself doesn’t route the bot session to another target. The API only responds with an array of `target` objects configured by the bot-admin. The client is responsible for transferring the session to one of the targets after receiving an escalation message from the bot. After the client routes the session to another target, the client must send back a transfer success or transfer failed message to the bot. If the client successfully transfers to an agent, the client must send a message with type `TransferSucceededRequestMessage` to the bot, as described in the next section. If it fails, the client must send a message with type `TransferFailedRequestMessage` to the bot. The client can then [End a Bot Session](https://developer.salesforce.com/docs/service/einstein-bot-api/guide/sdk-basic-bot-client-end-session.md). Here is an example of a transfer success message from the client to the bot. ```java // Build the message. AnyRequestMessage message = **new** **** TransferSucceededRequestMessage() .type(TransferSucceededRequestMessage.TypeEnum.TRANSFERSUCCEEDED) .sequenceId(System.*currentTimeMillis*()); // Build the request. BotSendMessageRequest botSendInitMessageRequest = BotRequest .withMessage(message) .build(); // Retrieve the RuntimeSessionId from the sessionId returned by the bot when // the session starts. RuntimeSessionId runtimeSessionId = new RuntimeSessionId(sessionId); // Send the transfer success message to the bot. BotResponse response = client .sendMessage(config, runtimeSessionId, botSendMessageRequest); ``` If you extract the text output from the bot’s response, the output must be empty for a transfer success response. Here’s an example of a transfer failed message from the client to the bot. If the bot sends an escalation message but the client isn’t able to transfer the customer to the directed source, the client must send a transfer failed message to the bot. ```java // Build the message. AnyRequestMessage message = TransferFailedRequestMessage() .type(TransferFailedRequestMessage.TypeEnum.TRANSFERFAILED) .reason(TransferFailedRequestMessage.ReasonEnum.NOAGENTAVAILABLE) .sequenceId(System.*currentTimeMillis*()); // Build the request. BotSendMessageRequest botSendInitMessageRequest = BotRequest .withMessage(message) .build(); // Retrieve the RuntimeSessionId from the sessionId returned by the bot when // the session starts. RuntimeSessionId runtimeSessionId = new RuntimeSessionId(sessionId); // Send the transfer failed message to the bot. BotResponse response = client .sendMessage(config, runtimeSessionId, botSendMessageRequest); ``` Here is an example text output from the bot if the client sends a transfer failed message. ```baash // Response output Unfortunately, there are no agents available at the moment ``` # Understand the Structure of the Bot Client Interfaces There are two interfaces of the bot client: `BasicChatbotClient` and `SessionManagedChatbotClient`. With the `BasicChatbotClient` interface, there’s a separate method to start and continue the session. You track the session using the bot’s runtime session ID. To start a session, you provide an `ExternalSessionId` of an external channel, such as a Slack or Twitter thread, to `startChatSession()`. Then the bot generates a unique internal session ID, which you use as the `RuntimeSessionId`, to send subsequent messages or end the active session. ```java // The basic bot client public interface BasicChatbotClient { BotResponse startChatSession(RequestConfig config, ExternalSessionId sessionId, BotSendMessageRequest requestEnvelope); BotResponse sendMessage(RequestConfig config, RuntimeSessionId sessionId, BotSendMessageRequest requestEnvelope); BotResponse endChatSession(RequestConfig config, RuntimeSessionId sessionId, BotEndSessionRequest requestEnvelope); Status getHealthStatus() } ``` The `SessionManagedChatbotClient` interface adds session management capabilities to the basic bot client. With the session-managed client, there’s no `startChatSession()` method and `RuntimeSessionId`. You start the session and send subsequent messages using the same method `sendMessage()`. You only need the `ExternalSessionId` since the client maps the external session ID to the internal one in the cache. For example, you could use a Slack thread ID to track all bot messages in a session without knowing the bot’s internal session ID. ```java // The session-managed bot client public interface SessionManagedChatbotClient { BotResponse sendMessage(RequestConfig config, ExternalSessionId sessionId, BotSendMessageRequest requestEnvelope); BotResponse endChatSession(RequestConfig config, ExternalSessionId sessionId, BotEndSessionRequest requestEnvelope); Status getHealthStatus() } ``` Here are the features that both basic bot and session-managed clients share. - The `RequestConfig` object contains the bot configuration variables. - The `BotSendMessageRequest` object contains the message and message options to start and continue a session. - The `BotEndSessionRequest` object contains the message to end the session. - The return type is `BotResponse` for all session requests. - The `getHealthStatus()` method returns the health status of the bot. - To create an instance of either client, you require a runtime URL and authentication mechanism. ```java // Create a basic bot client. BasicChatbotClient client = ChatbotClients.basic() .basePath(YOUR_RUNTIME_URL) .authMechanism(YOUR_OAUTH_MECHANISM) .build(); // Create a session-managed client. SessionManagedChatbotClient client = ChatbotClients .sessionManaged() .basicClient(ChatbotClients.basic() .basePath(YOUR_RUNTIME_URL) .authMechanism(YOUR_OAUTH_MECHANISM) .build()) .cache(new RedisCache(YOUR_TTL_SECONDS,YOUR_REDIS_URL)) .build(); ``` # Use the Basic Bot Client This section provides code samples of running a bot session with the `BasicChatbotClient` interface. # Use the Session-Managed Bot Client This section provides code samples of running a bot session with the `SessionManagedChatbotClient` interface. ## Set Up the Client The `SessionManagedChatbotClient` adds session management capabilities on top of the `BasicChatbotClient`. Therefore, to create an instance of the session-managed client, pass `BasicChatbotClient` as a parameter to the `SessionManagedChatbotClient`. See [Use the Basic Bot Client](https://developer.salesforce.com/docs/service/einstein-bot-api/guide/sdk-use-basic-bot-client.md). ```java // Create a JWT Bearer authentication mechanism. AuthMechanism oAuth = JwtBearerOAuth.with() .privateKeyFilePath(privateKeyFilePath) .loginEndpoint(loginEndpoint) .connectedAppId(connectedAppId) .connectedAppSecret(secret) .userId(userId) .build(); // Create a session-managed client. SessionManagedChatbotClient client = ChatbotClients .sessionManaged() .basicClient(ChatbotClients.basic() .basePath(basePath) .authMechanism(oAuth) .build()) .cache(new RedisCache(YOUR_TTL_SECONDS,YOUR_REDIS_URL)) .build(); ``` If you are running the client locally, you can use `InMemoryCache(YOUR_TTL_SECONDS)` instead of `RedisCache(YOUR_TTL_SECONDS,YOUR_REDIS_URL)` for the `.cache` value. The `InMemoryCache` is the better caching method when running the client locally but for production use `RedisCache` or your own distributed cache. Then create a `RequestConfig` object and add the bot ID, org ID, ForceConfig Endpoint that you already set up in the [Set Up the Configuration Variables](https://developer.salesforce.com/docs/service/einstein-bot-api/guide/sdk-set-up.md) section. ```java RequestConfig config = RequestConfig.with() .botId(botId) .orgId(orgId) .forceConfigEndpoint(forceConfigEndPoint) .build(); ``` ## Send Messages The session-managed client starts a new session and sends subsequent messages using the same method instead of two separate methods. A session is tracked by a combination of the external session ID, bot ID, and org ID. If the client sends a message request with a new combination of those IDs, the bot starts a new session. If the combination exists, the message is part of an active session. ```java // Retrieve the external session ID from the external channel. ExternalSessionId externalSessionId = new ExternalSessionId(UUID.randomUUID().toString()); // Build the request to send a message to the bot. BotSendMessageRequest sendMessageRequest = BotRequest .withMessage(buildTextMessage(message)) .build(); // Send the request using the external session ID. BotResponse response = client .sendMessage(config, externalSessionId, sendMessageRequest); ``` ## Retrieve the Bot Response The `BotResponse` object is the same as the one of the `BasicChatbotClient` so you can [Retrieve the Bot’s Response](https://developer.salesforce.com/docs/service/einstein-bot-api/guide/sdk-basic-bot-client-retrieve-bots-response.md) the same way. ## End the Bot Session The code is the same as [End a Bot Session](https://developer.salesforce.com/docs/service/einstein-bot-api/guide/sdk-basic-bot-client-end-session.md) for the `BasicChatbotClient` except that you pass `externalSessionId` instead of the bot’s internal session ID. ```java // Build the request to end the bot session. BotEndSessionRequest botEndSessionRequest = BotRequest .withEndSession(EndSessionReason.USERREQUEST).build(); // Use the external session ID of the external channel. ExternalSessionId externalSessionId = new ExternalSessionId(UUID.randomUUID().toString()); // Send the request to the bot to end the session. BotResponse endSessionResponse = client .endChatSession(config, externalSessionId, botEndSessionRequest); ``` # View and Publish Metrics You can go to `http://://actuator/metrics` to view the channel connector framework metrics. To publish the metrics to an external monitoring system, refer to the instructions for your monitoring system in the [Micrometer doc](https://micrometer.io/docs). See [NewRelicMetricsExportAutoConfiguration](https://github.com/forcedotcom/einstein-bot-channel-connector/blob/master/examples/ui-connector/src/main/java/com/salesforce/einsteinbot/connector/example/NewRelicMetricsExportAutoConfiguration.java) for an example of publishing the metrics to New Relic.