# API

***

## 1. Prediction API

* POST `/api/v1/prediction/{your-chatflowid}`

Request Body

<table><thead><tr><th width="192">Key</th><th width="347">Description</th><th>Type</th><th>Required</th></tr></thead><tbody><tr><td>question</td><td>User's question</td><td>string</td><td>Yes</td></tr><tr><td>overrideConfig</td><td>Override existing flow configuration</td><td>object</td><td>No</td></tr><tr><td>history</td><td>Prepend history messages at the start of conversation</td><td>array</td><td>No</td></tr></tbody></table>

You can use the chatflow as API and connect to frontend applications.

<figure><img src="/files/IqlXIvG2zZBKqOQyBqPG" alt=""><figcaption></figcaption></figure>

### Override Config

You also have the flexibility to override input configuration with **overrideConfig** property.

<figure><img src="/files/OixAXVo0Ch8eeIoJpZ9Z" alt=""><figcaption></figcaption></figure>

{% tabs %}
{% tab title="Python" %}

```python
import requests
API_URL = "http://<yourtenant>.tailwinds.innovativesol.com/api/v1/prediction/&#x3C;chatlfowid>"

def query(payload):
    response = requests.post(API_URL, json=payload)
    return response.json()
    
output = query({
    "question": "Hey, how are you?",
    "overrideConfig": {
        "sessionId": "123",
        "returnSourceDocuments": true
    }
})
```

{% endtab %}

{% tab title="Javascript" %}

```javascript
async function query(data) {
    const response = await fetch(
        "http://<yourtenant>.tailwinds.innovativesol.com/api/v1/prediction/<chatlfowid>",
        {
            method: "POST",
            headers: {
                "Content-Type": "application/json"
            },
            body: JSON.stringify(data)
        }
    );
    const result = await response.json();
    return result;
}

query({
    "question": "Hey, how are you?",
    "overrideConfig": {
        "sessionId": "123",
        "returnSourceDocuments": true
    }
}).then((response) => {
    console.log(response);
});
```

{% endtab %}
{% endtabs %}

### History

You can prepend history messages to give some context to LLM. For example, if you want the LLM to remember user's name:

{% tabs %}
{% tab title="Python" %}

```python
import requests
API_URL = "http://localhost:3000/api/v1/prediction/&#x3C;chatlfowid>"

def query(payload):
    response = requests.post(API_URL, json=payload)
    return response.json()
    
output = query({
    "question": "Hey, how are you?",
    "history": [
        {
            "role": "apiMessage",
            "content": "Hello how can I help?"
        },
        {
            "role": "userMessage",
            "content": "Hi my name is Brian"
        },
        {
            "role": "apiMessage",
            "content": "Hi Brian, how can I help?"
        },
    ]
})
```

{% endtab %}

{% tab title="Javascript" %}

```javascript
async function query(data) {
    const response = await fetch(
        "http://localhost:3000/api/v1/prediction/<chatlfowid>",
        {
            method: "POST",
            headers: {
                "Content-Type": "application/json"
            },
            body: JSON.stringify(data)
        }
    );
    const result = await response.json();
    return result;
}

query({
    "question": "Hey, how are you?",
    "history": [
        {
            "role": "apiMessage",
            "content": "Hello how can I help?"
        },
        {
            "role": "userMessage",
            "content": "Hi my name is Brian"
        },
        {
            "role": "apiMessage",
            "content": "Hi Brian, how can I help?"
        },
    ]
}).then((response) => {
    console.log(response);
});
```

{% endtab %}
{% endtabs %}

### Persists Memory

If the chatflow contains [Memory](/readme/chatflows/langchain/memory.md#memory-nodes) nodes, you can pass a `sessionId` to persists the state of the conversation, so the every subsequent API calls will have context about previous conversation. Otherwise, a new session will be generated each time.

{% tabs %}
{% tab title="Python" %}

```python
import requests
API_URL = "http://<yourtenant>.tailwinds.innovativesol.com/api/v1/prediction/&#x3C;chatlfowid>"

def query(payload):
    response = requests.post(API_URL, json=payload)
    return response.json()
    
output = query({
    "question": "Hey, how are you?",
    "overrideConfig": {
        "sessionId": "123"
    } 
})
```

{% endtab %}

{% tab title="Javascript" %}

```javascript
async function query(data) {
    const response = await fetch(
        "http://<yourtenant>.tailwinds.innovativesol.com/api/v1/prediction/<chatlfowid>",
        {
            method: "POST",
            headers: {
                "Content-Type": "application/json"
            },
            body: JSON.stringify(data)
        }
    );
    const result = await response.json();
    return result;
}

query({
    "question": "Hey, how are you?",
    "overrideConfig": {
        "sessionId": "123"
    }
}).then((response) => {
    console.log(response);
});
```

{% endtab %}
{% endtabs %}

### Image Uploads

When **Allow Image Upload** is enabled, images can be uploaded from chat interface.

{% tabs %}
{% tab title="Python" %}

```python
import requests
API_URL = "http://<yourtenant>.tailwinds.innovativesol.com/api/v1/prediction/&#x3C;chatlfowid>"

def query(payload):
    response = requests.post(API_URL, json=payload)
    return response.json()
    
output = query({
    "question": "Can you describe the image?",
    "uploads": [
        {
            "data": 'data:image/png;base64,iVBORw0KGgdM2uN0', # base64 string or url
            "type": 'file', # file | url
            "name": 'image.png',
            "mime": 'image/png'
        }
    ]
})
```

{% endtab %}

{% tab title="Javascript" %}

```javascript
async function query(data) {
    const response = await fetch(
        "http://<yourtenant>.tailwinds.innovativesol.com/api/v1/prediction/<chatlfowid>",
        {
            method: "POST",
            headers: {
                "Content-Type": "application/json"
            },
            body: JSON.stringify(data)
        }
    );
    const result = await response.json();
    return result;
}

query({
    "question": "Can you describe the image?",
    "uploads": [
        {
            "data": 'data:image/png;base64,iVBORw0KGgdM2uN0', //base64 string or url
            "type": 'file', //file | url
            "name": 'Flowise.png',
            "mime": 'image/png'
        }
    ]
}).then((response) => {
    console.log(response);
});
```

{% endtab %}
{% endtabs %}

### Speech to Text

When **Speech to Text** is enabled, users can speak directly into microphone and speech will be transcribed into text.

<div align="left"><figure><img src="/files/zOCYcb7f1Mr6huQfTVlq" alt="" width="563"><figcaption></figcaption></figure> <figure><img src="/files/rFkrPfSArkZ8k4KmOipi" alt="" width="431"><figcaption></figcaption></figure></div>

{% tabs %}
{% tab title="Python" %}

```python
import requests
API_URL = "http://<yourtenant>.tailwinds.innovativesol.com/api/v1/prediction/&#x3C;chatlfowid>"

def query(payload):
    response = requests.post(API_URL, json=payload)
    return response.json()
    
output = query({
    "uploads": [
        {
            "data": 'data:audio/webm;codecs=opus;base64,GkXf', #base64 string
            "type": 'audio',
            "name": 'audio.wav',
            "mime": 'audio/webm'
        }
    ]
})
```

{% endtab %}

{% tab title="Javascript" %}

```javascript
async function query(data) {
    const response = await fetch(
        "http://<yourtenant>.tailwinds.innovativesol.com/api/v1/prediction/<chatlfowid>",
        {
            method: "POST",
            headers: {
                "Content-Type": "application/json"
            },
            body: JSON.stringify(data)
        }
    );
    const result = await response.json();
    return result;
}

query({
    "uploads": [
        {
            "data": 'data:audio/webm;codecs=opus;base64,GkXf', //base64 string
            "type": 'audio',
            "name": 'audio.wav',
            "mime": 'audio/webm'
        }
    ]
}).then((response) => {
    console.log(response);
});
```

{% endtab %}
{% endtabs %}

### Authentication

You can assign an API key to the prediction API from the UI. Refer [Chatflows and APIs](/readme/api/chatflow-level.md) for more details.

The Authorization header must be provided with the correct API key specified during a HTTP call.

```json
"Authorization": "Bearer <your-api-key>"
```

## 2. Vector Upsert API

* POST `/api/v1/vector/upsert/{your-chatflowid}`

Request Body

<table><thead><tr><th width="192">Key</th><th width="559">Description</th><th>Type</th><th>Required</th></tr></thead><tbody><tr><td>overrideConfig</td><td>Override existing flow configuration</td><td>object</td><td>No</td></tr><tr><td>stopNodeId</td><td>Node ID of the vector store. When you have multiple vector stores in a flow, you might not want to upsert all of them. Specifying <code>stopNodeId</code> will ensure only that specific vector store node is upserted.</td><td>array</td><td>No</td></tr></tbody></table>

### Document Loaders with Upload

Some document loaders in Tailwinds allow user to upload files:

<figure><img src="/files/3TgxARL88PiGje9iloHn" alt=""><figcaption></figcaption></figure>

If the flow contains [Document Loaders](/readme/chatflows/langchain/document-loaders.md) with Upload File functionality, the API looks slightly different. Instead of passing body as **JSON**, **form-data** is being used. This allows you to upload any files to the API.

{% hint style="info" %}
It is user's responsibility to make sure the file type is compatible with the expected file type from document loader. For example, if a Text File Loader is being used, you should only upload file with `.txt` extension.
{% endhint %}

{% tabs %}
{% tab title="Python" %}

```python
import requests

API_URL = "http://<yourtenant>.tailwinds.innovativesol.com/api/v1/vector/upsert/<chatlfowid>"

# use form data to upload files
form_data = {
    "files": ('state_of_the_union.txt', open('state_of_the_union.txt', 'rb'))
}

body_data = {
    "returnSourceDocuments": True
}

def query(form_data):
    response = requests.post(API_URL, files=form_data, data=body_data)
    print(response)
    return response.json()

output = query(form_data)
print(output)
```

{% endtab %}

{% tab title="Javascript" %}

```javascript
// use FormData to upload files
let formData = new FormData();
formData.append("files", input.files[0]);
formData.append("returnSourceDocuments", true);

async function query(formData) {
    const response = await fetch(
        "http://localhost:3000/api/v1/vector/upsert/<chatlfowid>",
        {
            method: "POST",
            body: formData
        }
    );
    const result = await response.json();
    return result;
}

query(formData).then((response) => {
    console.log(response);
});
```

{% endtab %}
{% endtabs %}

### Document Loaders without Upload

For other [Document Loaders](/readme/chatflows/langchain/document-loaders.md) nodes without Upload File functionality, the API body is in **JSON** format similar to [Prediction API](#prediction-api).

{% tabs %}
{% tab title="Python" %}

```python
import requests

API_URL = "http://<yourtenant>.tailwinds.innovativesol.com/api/v1/vector/upsert/<chatlfowid>"

def query(form_data):
    response = requests.post(API_URL, json=payload)
    print(response)
    return response.json()

output = query({
    "overrideConfig": { # optional
        "returnSourceDocuments": true
    }
})
print(output)
```

{% endtab %}

{% tab title="Javascript" %}

```javascript
async function query(data) {
    const response = await fetch(
        "http://localhost:3000/api/v1/vector/upsert/<chatlfowid>",
        {
            method: "POST",
            headers: {
                "Content-Type": "application/json"
            },
            body: JSON.stringify(data)
        }
    );
    const result = await response.json();
    return result;
}

query({
    "overrideConfig": { // optional
        "returnSourceDocuments": true
    }
}).then((response) => {
    console.log(response);
});
```

{% endtab %}
{% endtabs %}

### Authentication

You can assign an API key to the prediction API from the UI. Refer [Chatflows and APIs](/readme/api/chatflow-level.md) for more details.

The Authorization header must be provided with the correct API key specified during a HTTP call.

```json
"Authorization": "Bearer <your-api-key>"
```

## 3. Message API

* GET `/api/v1/chatmessage/{your-chatflowid}`
* DELETE `/api/v1/chatmessage/{your-chatflowid}`

Query Parameters

| Param     | Type   | Value       |
| --------- | ------ | ----------- |
| sessionId | string |             |
| sort      | enum   | ASC or DESC |
| startDate | string |             |
| endDate   | string |             |

### Authentication

Message API is restricted to only Tailwinds admin user. Basic authentication must be provided in the headers if Tailwinds instance has been configured with `USERNAME` and `PASSWORD`. Refer [https://github.com/innovativeSol/tailwinds-docs/blob/main/using-tailwinds/broken-reference/README.md](https://github.com/innovativeSol/tailwinds-docs/blob/main/using-tailwinds/broken-reference/README.md "mention") for more details.

{% tabs %}
{% tab title="Node" %}

```javascript
"Authorization": "Basic " + Buffer.from(username + ":" + password).toString('base64')
```

{% endtab %}

{% tab title="Browser" %}

```javascript
'"Authorization": "Basic " + btoa(username + ":" + password)
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

from requests.auth import HTTPBasicAuth
res = requests.get('http://<yourtenant>.tailwinds.innovativesol.com/api/v1/chatmessage/{your-chatflowid}', auth=HTTPBasicAuth(username, password))
```

{% endtab %}
{% endtabs %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://tailwindsdocs.innovativesol.com/readme/api.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
