tftsr-devops_investigation/GenAI API User Guide.md

489 lines
60 KiB
Markdown
Raw Normal View History

**A User Guide to GenAI API**
**Revision:** 3.4
**Change History**
| Date | Version | Author | Changes |
| :---- | :---- | :---- | :---- |
| 08-22-2023 | 1.0 | Dipjyoti Bisharad | Initial Draft |
| 08-24-2023 | 1.1 | Jahnavi Alike | Updated sessionId usage process. |
| 12-12-2023 | 1.2 | Sunil Vurandur | Updated details on different model types |
| 03-28-2024 | 1.3 | Jahnavi Alike | Text rearrangement |
| 04-23-2024 | 1.4 | Dipjyoti Bisharad | Introduced additional args field in response |
| 05-08-2024 | 2.0 | Dipjyoti Bisharad | Updated to v2 of chat API |
| 05-17-2024 | 2.1 | Dipjyoti Bisharad | Added Gemini 1.5 Pro |
| 06-25-2024 | 2.2 | Dipjyoti Bisharad | Added disclaimer on omitting the userId from payload |
| 06-27-2024 | 2.3 | Dipjyoti Bisharad | Added the datastoreId to the request payload. Introduced GPT 4 Omni, Claude Sonnet. Replaced Gemini 1.5 Pro with Gemini 1.5 Flash. Deprecation of legacy /chat. |
| 07-10-2024 | 2.4 | Dipjyoti Bisharad | Added modelConfig in the chat payload. Added application identifier x-msi-genai-client in header. Upload files to a chat session |
| 08-09-2024 | 2.5 | Dipjyoti Bisharad | Added note regarding impersonation with API keys |
| 08-12-2024 | 2.6 | Dipjyoti Bisharad | Added userId usage for the /upload endpoint |
| 09-16-2024 | 2.7 | Dipjyoti Bisharad | Added endpoints for delete message and retrieve session messages |
| 11-15-2024 | 2.8 | Dipjyoti Bisharad | Updated contacts |
| 11-18-2024 | 2.9 | Girish Manivel | Added API error codes |
| 01-09-2025 | 3.0 | Anjali Kamath | Added Init Datastore API details |
| 01-15-2025 | 3.1 | Vibin Jacob | Added default Model config values |
| 01-30-2025 | 3.2 | Vibin Jacob | Added Nova Lite |
| 03-04-2025 | 3.3 | Anjali Kamath | Updated Model details |
| 03-27-2025 | 3.4 | Vibin Jacob | Updated Model details |
| 05-12-2025 | 3.5 | Vibin Jacob | Google Search Agent |
#
# **People** {#people}
| Name | Role | Email |
| ----- | ----- | ----- |
| Manminder Kaur Sardarni | Dev Manager | manminderkaur.sardarni@motorolasolutions.com |
| Suma Rebbapragada | Product Manager | lakshmisuma.rebbapragada@motorolasolutions.com |
| Anjali Kamath | Lead Engineer | anjali.kamath@motorolasolutions.com |
| Sri Chand Jasti | Sr Mgr, IT AIML | srichand.jasti@motorolasolutions.com |
**Table of Contents**
---
**[People 3](#people)**
[**1\. Abstract 5**](#abstract)
[2\. The API Key 5](#the-api-key)
[**3\. Properties of the API Key 7**](#properties-of-the-api-key)
[**4\. API Reference 7**](#api-reference)
[4.1. Chat Endpoint 7](#chat-endpoint)
[4.2. File upload Endpoint 10](#file-upload-endpoint)
[4.3. Get Session Messages Endpoint 11](#get-session-messages/datastore-files-endpoint)
[4.4. Delete Message Endpoint 12](#delete-message-endpoint)
4.5. Initialise Datastore Endpoint 13
1. # Abstract {#abstract}
This document is a user guide to interact with the API provided by MSI GenAI, which can be used to programmatically send a prompt and receive a response from AI Models. The endpoint can do session management, so the user needs to pass only the current prompt and session ID (more details below), and the past contexts will be automatically retained.
The API is exposed at the following link: [https://genai-service.stage.commandcentral.com/app-gateway/api/v2/chat](https://genai-service.stage.commandcentral.com/app-gateway/api/v2/chat)
To interact with the API, an API key is required, which can be obtained from the steps listed below.
**Note: In this v2 release, the endpoint has been changed to /api/v2/chat, which will support a long-lived API key. The former endpoint /chat stands deprecated.**
2. # The API Key {#the-api-key}
Follow these steps to get an API key for the endpoint.
1. Log in to the [MSI GenAI Portal](https://msi-genai.stage.commandcentral.com/login)
2. After logging in, click on the name in the top right corner of the screen and click on the **Generate API Key**.
![][image1]
3. Click on the **Copy & Close** button. This automatically copies the token to the clipboard. This token will be visible only once and is not saved or logged anywhere in the backend
![][image2]
4. ~~The token for the legacy /chat endpoint can be obtained by clicking the **Auth Token (Legacy)** button.~~
**NOTE:** API usage cost is limited to $50 per user/month. The users keys will be disabled once they cross the limit in a normal scenario.
3. # Properties of the API Key {#properties-of-the-api-key}
1) Visible only once.
2) Valid for 3 months.
3) The API key CANNOT be used to request content on behalf of other users, however, the owner of the key shall always be logged for audit purposes.
API keys will ordinarily not be able to request content on behalf of other users. Reach out to any of the contacts listed if a key has to be revoked.
4) Upcoming feature to allow customizing the token validity as well as self-service token revocation.
4. # API Reference {#api-reference}
1. ## Chat Endpoint {#chat-endpoint}
* Host: [https://genai-service.stage.commandcentral.com/app-gateway](https://genai-service.stage.commandcentral.com/app-gateway)
* Endpoint: /api/v2/chat
* HTTP Type: POSTHeaders:
**x-msi-genai-api-key: \<INSERT API KEY HERE\>**
Content-Type: application/json
X-msi-genai-client (optional): \<some-unique-app-identifier\>
* Request Body:
{
"userId": "core-id@motorolasolutions.com",
"model": "VertexGemini",
"prompt": "Who plays the best piano?",
"system": "",
"sessionId": "c2e07ae5-4d6b-48e6-b035-6a8aefb57321",
"datastoreId": "a1c0e7f2-a01a-4ee4-b5fb-e3362bd1f302",
"modelConfig": {
"temperature": 0.5,
"max\_tokens": 800,
"top\_p": 1,
"frequency\_penalty": 0,
"presence\_penalty": 0
}
}
The different fields of the request body have the following usage.
* userId (optional): The email address of the user in the **CORE ID** format.
**Note:** *In case this field is not provided with the payload, all the usages and subsequent costs will be mapped to the user who created the token.*
* model (mandatory): The model to be used for the AI. The following options are available:
- Use model: **ChatGPT4o** to access GPT4 Omni
- Use model: **ChatGPT4o-mini** to access GPT4o Omni Mini
- Use model: **ChatGPT-o3-mini** to access GPT o3 Omni
- Use model: **Gemini-2\_0-Flash-001** to access Gemini 2.0 Flash
- Use model: **Gemini-2\_5-Flash** to access Gemini 2.5 Flash
- Use model: **Claude-Sonnet-3\_7** to access Claude-Sonnet 3.7.
- Use model: **Openai-gpt-4\_1-mini** to access OpenAI GPT 4.1 Mini.
- Use model: **Openai-o4-mini** to access OpenAI o4 Mini.
- Use model: **Claude-Sonnet-4** to access Claude Sonnet 4\.
- Use model: **ChatGPT-o3-pro** to access ChatGPT o3 Pro.
- Use model: **OpenAI-ChatGPT-4\_1** to access OpenAI ChatGPT 4.1.
- Use model: **OpenAI-GPT-4\_1-Nano** to access OpenAI ChatGPT 4.1 Nano.
- Use model: **ChatGPT-5** to access OpenAI ChatGPT5
- Use model: **VertexGemini** to access Gemini 2.0 Flash 001
- Use model: **ChatGPT-5\_1** to access ChatGPT 5.1
- Use model: **ChatGPT-5\_1-chat** to access ChatGPT 5.1 Chat
- Use model: **ChatGPT-5\_2-Chat** to access ChatGPT 5.2 Chat
- Use model: **Gemini-3\_Pro-Preview** to access Gemini 3.1 Pro
- Use model: **Gemini-3\_1-flash-lite-preview** to access Gemini 3.1 Flash Lite
**File uploads are currently supported for models VertexGemini, ChatGPT4o, and Claude-Sonnet (Claude-Sonnet supports image files only)**
* Supported Files
1.
| Model | Supported Files Extensions |
| :---- | :---- |
| **Gemini-3\_Pro-Preview** | **Image:** JPEG, JPG, PNG or WEBP format. **Video:** MP4, WEBM, MKV or MOV format. **Document:** PDF or TXT format. **Audio:** MP3, MPGA, WAV, WEBM, M4A, OPUS, AAC, FLAC or PCM format. |
| **ChatGPT 4o** | Text, image |
| **ChatGPT 4.1 Mini** | text and vision |
| **ChatGPT 5.2 Chat** | |
| **ChatGPT o4 Mini** | Text, image |
| **Claude Sonnet 4** | |
2. Check model Access and privacy for model accessibility
* prompt (mandatory): The plain text query.
* sessionId (optional): All the messages linked to a sessionId are treated as part of the same conversation. **The sessionId should not be passed for the first message.** The AI engine generates a session ID after the first successful prompt, and that value should be used in subsequent API calls.
* system (optional): Optional system message that can be configured for the model. It will only work with the models that support it. Ignored for non-supported models.
* datastoreId (optional): Optional DatastoreId that can be passed to refer to the files of a datastore within your chat session.
* modelConfig (optional): Expert settings to tune the model parameters.
If model parameters are not mentioned in the request payload, they will set by default as below:
temperature: 0.7
max\_tokens:(800 Azure OpenAI),(4000 Gemini),(1024 AWS)
top\_p: 1.0
Top\_k: (NA Azure OpenAI), (32 Gemini), (250 AWS)
frequency\_penalty: 0
presence\_penalty:0
* Response Body:
{
"status": true,
"success": true,
"sessionId": String,
"sessionTitle": String,
"msg": Text,
"valid\_response": Boolean,
"initialPrompt": Boolean,
"args": JSON
}
The different fields of the response body have the following usage.
* status: states whether the API call is successful or not
* success: always true (internal reference key).
* sessionId: UUID for a session. One can use the same sessionId to maintain a conversation in the same session
* sessionTitle: Title created for the session
* msg: Actual response from AI models for users prompt
* valid\_response: always true (internal reference key).
* initialPrompt: states whether the user is starting a new session or an old one. If the user sends a message for the first time in a new session, then this will be true; else false.
* args: Contains metadata about the response
* Error Response Body:
{
status: false,
msg: String (Error Msg)
}
The curl command for the request looks like this:
curl \-X POST \-H "x-msi-genai-api-key: \<key-value\>" \-H "Content-Type: application/json" https://genai-service.stage.commandcentral.com/app-gateway/api/v2/chat \-d '{"userId": "String", "model": "String", "prompt": "Text", "sessionId": "String", "datastoreId": "String"}'
Here,
- \-X refers to the HTTP Method we want to use
- \-H refers to a header, you can use multiple headers by using multiple -H
- \-d refers to the data or body of the request
2. ## File upload Endpoint {#file-upload-endpoint}
* Host: \-5
* Endpoint: /api/v2/upload/\<SESSION-ID\>?userId=\<CORE-ID\>@motorolasolutions.com
* HTTP Type: POST
* Headers:
**x-msi-genai-api-key: \<INSERT API KEY HERE\>**
Content-Type: multipart/form-data
X-msi-genai-client (optional): \<some-unique-app-identifier\>
**Note:**
* The SESSION-ID is mandatory, and hence a file **CANNOT** be the first message in chat history. Obtain a session id by initiating a chat session specified in 4.1
* The userId is optional and by default is set to the user who created the token.
* The owner of the SESSION-ID should match the userId
The curl command for the request looks like this:
curl \-X POST \--location 'https://genai-service.stage.commandcentral.com/app-gateway/api/v2/upload/\<SESSION-ID\>' \\
\--header 'x-msi-genai-api-key: \<key-value\>' \\
\--form 'file=@"/var/pdf/myfile.pdf"'
3. ## Get Session Messages/Datastore Files Endpoint {#get-session-messages/datastore-files-endpoint}
* Host: [https://genai-service.stage.commandcentral.com/app-gateway](https://genai-service.stage.commandcentral.com/app-gateway)
* Endpoint: /api/v2/getSessionMessages/\<SESSION-ID\>?userId=\<CORE-ID\>@motorolasolutions.com\&page=1\&limit=10
* HTTP Type: GET
* Headers:
**x-msi-genai-api-key: \<INSERT API KEY HERE\>**
X-msi-genai-client (optional): \<some-unique-app-identifier\>
**Note:**
* The SESSION-ID is mandatory. For Datastores, pass the datastore id as SESSION-ID.
* The userId is optional and by default is set to the user who created the token.
* The owner of the SESSION-ID should match the userId.
* The page & limit are optional and meant for pagination for large message sessions. By default, all messages are returned.
The curl command for the request looks like this:
curl \-X GET \--location 'https://genai-service.stage.commandcentral.com/app-gateway/api/v2/getSessionMessages/\<SESSION-ID\>?page=1\&limit=2' \\
\--header 'x-msi-genai-api-key: \<key-value\>'
The response structure is
```
{
"status": true,
"TotalSessionLength": "134",
"data": [
,
{
"id": 1,
"msg": "hi",
"role": "user",
"type": "text"
},
{
"id": 2,
"msg": "Hi! 😊 How can I assist you today? \n",
"role": "assistant",
"type": "text"
}
]
}
```
**Note:**
* The status will be true if the data is successfully retrieved; false otherwise
* The TotalSessionLength gives the total number of messages in the specified session.
* The data has the list of messages (as per page and limit specified, if any) ordered by increasing order of chronology (most recent message is at the last of the array).
* Each msg has a unique id which can be used to delete the message from the session.
4. ## Delete Message Endpoint {#delete-message-endpoint}
* Host: [https://genai-service.stage.commandcentral.com/app-gateway](https://genai-service.stage.commandcentral.com/app-gateway)
* Endpoint: /api/v2/entry/\<MSG-ID\>?userId=\<CORE-ID\>@motorolasolutions.com
* HTTP Type: DELETE
* Headers:
**x-msi-genai-api-key: \<INSERT API KEY HERE\>**
X-msi-genai-client (optional): \<some-unique-app-identifier\>
**Note:**
* The MSG-ID is mandatory
* The userId is optional and by default is set to the user who created the token.
* The owner of the MSG-ID should match the userId
The curl command for the request looks like this:
curl \-X DELETE \--location 'https://genai-service.stage.commandcentral.com/app-gateway/api/v2/entry/\<MSG-ID\>' \\
\--header 'x-msi-genai-api-key: \<key-value\>'
5. ## Initialise Datastore Endpoint
Host: [https://genai-service.stage.commandcentral.com/app-gateway](https://genai-service.stage.commandcentral.com/app-gateway)
Endpoint: /api/v2/initDataStore/datastore/\<DATASTORE-NAME\>
HTTP Type: POST
Headers:
**x-msi-genai-api-key: \<INSERT API KEY HERE\>**
X-msi-genai-client (optional): \<some-unique-app-identifier\>
**Note:**
* The DATASTORE-NAME is mandatory
* Datastore ID can be retrieved from the response payload
* You can upload files to the datastore using the file upload endpoint.
The curl command for the request looks like this:
curl \-X POST \--location 'genai-service.stage.commandcentral.com/app-gateway/api/v2/initDataStore/datastore/\<DATASTORE-NAME\>' \\
\--header 'x-msi-genai-api-key: \<key-value\>'
6. ## Get Chat Sessions Endpoint
* Host: [https://genai-service.stage.commandcentral.com/app-gateway](https://genai-service.stage.commandcentral.com/app-gateway)
* Endpoint: /api/v2/getChatSessions/\<MODEL\>
* HTTP Type: GET
* Headers:
**x-msi-genai-api-key: \<INSERT API KEY HERE\>**
X-msi-genai-client (optional): \<some-unique-app-identifier\>
**Note:**
* The MODEL is mandatory PARAMETER.
The curl command for the request looks like this:
curl \-X GET \--location 'https://genai-service.stage.commandcentral.com/app-gateway/api/v2/getChatSessions/\<MODEL\>' \\
\--header 'x-msi-genai-api-key: \<key-value\>'
The response structure is
```
{
"status": true,
"msg": "Session fetched successfully",
"data": [
,
{
"sessionId": "7cads-a9123-1a1ddd",
"sessionTittle": "hi",
"sessionchatinstruction": "",
"total_tokens": 0
},
{
"sessionId": "8bads-a9123-1a1ddd",
"sessionTittle": "why is sky blue",
"sessionchatinstruction": "",
"total_tokens": 0
},
],
"user_cost": "0"
}
```
**Note:**
* The status will be true if the data is successfully retrieved; false otherwise
* The user\_cost will return the monthly usage cost value of the portal.
* The data has the list of messages (as per page and limit specified, if any) ordered by increasing order of chronology (most recent message is at the last of the array).
* Each sessionTitle has a unique sessionId
#
5. # API Error Codes
| General Error Codes | |
| ----- | :---- |
| 400 Bad Request | The request was malformed or contained invalid parameters. |
| 401 Unauthorized | The user is not authenticated or lacks permission to perform the requested action. |
| 403 Forbidden | The user is authenticated but lacks the necessary permissions for the requested action. |
| 404 Not Found | The requested resource (model, user, API key, session, etc.) was not found. |
| 405 Method Not Allowed | The requested method (GET, POST, PUT, DELETE) is not allowed for the resource. |
| 409 Conflict | The requested action cannot be completed due to a conflict, such as attempting to create a duplicate resource. |
| 500 Internal Server Error | An unexpected error occurred on the server. |
| 502 Bad Gateway | The server received an invalid response from a downstream server. |
| 503 Service Unavailable | The server is currently unavailable. |
| Error Codes Breakdown | |
| :---: | ----- |
**fileHandlerService:**
| uploadFile | |
| :---- | :---- |
| 400 | Invalid Datastore ID. |
| 500 | Error while uploading the file. |
| **deleteMessage** | |
| 400 | Invalid request, missing msgId. |
| 401 | Unauthorized. |
| 404 | Message not found. |
| 500 | Internal error while deleting the message. |
**usersHandler:**
| getSessionMessages | |
| :---- | :---- |
| 400 | Invalid request body, missing sessionId. |
| 401 | Unauthorized access to the session. |
| 500 | Internal error while fetching session messages. |
| **chat** | |
| 400 | Invalid request, missing model or prompt, or model not found. |
| 500 | Internal error while processing the chat request. |
**Error Response Format:**
All error responses follow a consistent format:
json
{
"status": false,
"msg": "Error message. Correlation ID: \<transactionId\>"
}
**Note:**
* \<transactionId\> is a unique identifier for the request.
* The error field might be present in certain error responses, providing additional details about the error.
6. # Model Access & Privacy
###
| Model Name (ID) | Friendly Name | Privacy Status |
| :---- | :---- | :---- |
| ChatGPT4o | GPT4 Omni | **Public** |
| ChatGPT4o-mini | GPT4o Omni Mini | **Private** |
| ChatGPT-o3-mini | GPT o3 Omni | **Private** |
| Gemini-2\_0-Flash-001 | Gemini 2.0 Flash | **Private** |
| Gemini-2\_5-Flash | Gemini 2.5 Flash | **Private** |
| Claude-Sonnet-3\_7 | Claude-Sonnet 3.7 | **Private** |
| Openai-gpt-4\_1-mini | OpenAI GPT 4.1 Mini | **Private** |
| Openai-o4-mini | OpenAI o4 Mini | **Public** |
| Claude-Sonnet-4 | Claude Sonnet 4 | **Public** |
| ChatGPT-o3-pro | ChatGPT o3 Pro | **Private** |
| OpenAI-ChatGPT-4\_1 | OpenAI ChatGPT 4.1 | **Private** |
| OpenAI-GPT-4\_1-Nano | OpenAI ChatGPT 4.1 Nano | **Private** |
| ChatGPT-5 | OpenAI ChatGPT5 | **Private** |
| VertexGemini | Gemini 2.0 Flash 001 | **Private** |
| ChatGPT-5\_1 | ChatGPT 5.1 | **Private** |
| ChatGPT-5\_1-chat | ChatGPT 5.1 Chat | **Private** |
| ChatGPT-5\_2-Chat | ChatGPT 5.2 Chat | **Public** |
| Gemini-3\_Pro-Preview | Gemini 3 Pro | **Private** |
[image1]: <data:image/png;base64,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
[image2]: <data:image/png;base64,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