Skip to main content

Overview

The RAG (Retrieval-Augmented Generation) API allows you to upload documents and create knowledge bases that your AI agents can search. Documents are uploaded to Google Cloud Storage and indexed in Vertex AI Search for semantic retrieval.
Workflow: First create a DataStore using POST /rag/datastores, then upload files to that DataStore using POST /rag/upload. Files are indexed asynchronously via background tasks.

Authentication

All RAG endpoints require API key authentication with specific scopes:
EndpointRequired Scope
POST /rag/datastoresrag:upload
GET /rag/datastoresrag:read
GET /rag/datastores/{id}/filesrag:read
DELETE /rag/datastores/{id}rag:delete
DELETE /rag/datastores/{id}/files/{filename}rag:delete
POST /rag/uploadrag:upload
GET /rag/uploadsrag:read
GET /rag/uploads/{upload_id}rag:read
Scopes Required: The rag:upload, rag:read, and rag:delete scopes must be explicitly enabled when creating API keys. They are not enabled by default.
Include your API key in the X-API-Key header:
X-API-Key: jns_live_YOUR_API_KEY_HERE
See Authentication for details.

Supported File Types

The following file types can be uploaded to RAG Datastores:
MIME TypeExtensionDescription
application/pdf.pdfPDF documents
application/vnd.openxmlformats-officedocument.wordprocessingml.document.docxMicrosoft Word documents
application/vnd.openxmlformats-officedocument.presentationml.presentation.pptxMicrosoft PowerPoint presentations
text/plain.txtPlain text files
text/html.htmlHTML documents
application/json.jsonJSON files
application/x-ndjson.jsonlNewline-delimited JSON
text/markdown.md, .markdownMarkdown documents
File Limits: Maximum 10MB per file, maximum 10 files per upload request.

Create DataStore

Create a new DataStore to organize uploaded documents.

Endpoint

POST https://api.junis.ai/api/external/rag/datastores

Request Body

FieldTypeRequiredDescription
display_namestringYesHuman-readable name for the DataStore (e.g., “Company Policies”)
descriptionstringNoOptional description of the DataStore’s purpose

Request Example

cURL
curl -X POST "https://api.junis.ai/api/external/rag/datastores" \
  -H "X-API-Key: jns_live_YOUR_API_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{
    "display_name": "Company Policies",
    "description": "HR policies and employee handbook"
  }'
Python
import requests

response = requests.post(
    "https://api.junis.ai/api/external/rag/datastores",
    headers={
        "X-API-Key": "jns_live_YOUR_API_KEY_HERE",
        "Content-Type": "application/json"
    },
    json={
        "display_name": "Company Policies",
        "description": "HR policies and employee handbook"
    }
)
print(response.json())
JavaScript
const response = await fetch('https://api.junis.ai/api/external/rag/datastores', {
  method: 'POST',
  headers: {
    'X-API-Key': 'jns_live_YOUR_API_KEY_HERE',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    display_name: 'Company Policies',
    description: 'HR policies and employee handbook'
  })
});
const data = await response.json();
console.log(data);

Response

Status Code: 200 OK
{
  "success": true,
  "datastore_id": "company-policies-a1b2c3d4",
  "display_name": "Company Policies",
  "description": "HR policies and employee handbook",
  "message": "DataStore 'Company Policies' created successfully"
}

Response Fields

FieldTypeDescription
successbooleanWhether creation was successful
datastore_idstringUnique ID for the DataStore (use this for uploads)
display_namestringHuman-readable name
descriptionstringDataStore description (null if not provided)
messagestringSuccess message

Error Responses

409 Conflict - DataStore Already Exists
{
  "detail": {
    "error": {
      "message": "DataStore with name 'Company Policies' already exists",
      "type": "conflict",
      "code": "datastore_exists",
      "existing_datastore_id": "company-policies-a1b2c3d4"
    }
  }
}

Upload Files

Upload files to an existing DataStore. Files are encoded as Base64 and indexed asynchronously.

Endpoint

POST https://api.junis.ai/api/external/rag/upload
DataStore Required: You must create a DataStore first using POST /rag/datastores. File uploads to non-existent DataStores will fail with a 404 error.

Request Body

FieldTypeRequiredDescription
datastore_idstringYesID of existing DataStore (from create response)
filesarrayYesArray of files to upload (min: 1, max: 10)
files[].filenamestringYesFilename with extension (e.g., “policy.pdf”)
files[].contentstringYesBase64-encoded file content
files[].content_typestringNoMIME type (auto-detected from extension if not provided)

Request Example

cURL
# First, encode your file as Base64
BASE64_CONTENT=$(base64 -i policy.pdf)

curl -X POST "https://api.junis.ai/api/external/rag/upload" \
  -H "X-API-Key: jns_live_YOUR_API_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{
    "datastore_id": "company-policies-a1b2c3d4",
    "files": [
      {
        "filename": "policy.pdf",
        "content": "'"${BASE64_CONTENT}"'"
      }
    ]
  }'
Python
import base64
import requests

# Read and encode file
with open("policy.pdf", "rb") as f:
    content = base64.b64encode(f.read()).decode()

response = requests.post(
    "https://api.junis.ai/api/external/rag/upload",
    headers={
        "X-API-Key": "jns_live_YOUR_API_KEY_HERE",
        "Content-Type": "application/json"
    },
    json={
        "datastore_id": "company-policies-a1b2c3d4",
        "files": [
            {
                "filename": "policy.pdf",
                "content": content
            }
        ]
    }
)
print(response.json())

Response

Status Code: 200 OK
{
  "success": true,
  "datastore_id": "company-policies-a1b2c3d4",
  "datastore_name": "Company Policies",
  "uploads": [
    {
      "upload_id": "550e8400-e29b-41d4-a716-446655440000",
      "filename": "policy.pdf",
      "status": "processing"
    }
  ],
  "message": "1 file(s) upload started"
}

Response Fields

FieldTypeDescription
successbooleanWhether upload was initiated
datastore_idstringTarget DataStore ID
datastore_namestringTarget DataStore display name
uploadsarrayStatus of each uploaded file
uploads[].upload_idstringUnique upload ID (UUID)
uploads[].filenamestringOriginal filename
uploads[].statusstringUpload status (processing)
messagestringSummary message

Error Responses

404 Not Found - DataStore Not Found
{
  "detail": {
    "error": {
      "message": "DataStore not found: invalid-datastore-id. Create it first using POST /rag/datastores",
      "type": "not_found",
      "code": "datastore_not_found"
    }
  }
}
400 Bad Request - Unsupported File Type
{
  "detail": {
    "error": {
      "message": "Unsupported file extension: .exe",
      "type": "validation_error",
      "code": "unsupported_file_type"
    }
  }
}
400 Bad Request - File Too Large
{
  "detail": {
    "error": {
      "message": "File size exceeds 10MB limit",
      "type": "validation_error",
      "code": "file_too_large"
    }
  }
}
400 Bad Request - Invalid Base64
{
  "detail": {
    "error": {
      "message": "Base64 decoding failed: Invalid padding",
      "type": "validation_error",
      "code": "invalid_base64"
    }
  }
}

List DataStores

Retrieve all DataStores in your organization.

Endpoint

GET https://api.junis.ai/api/external/rag/datastores

Request Example

cURL
curl -X GET "https://api.junis.ai/api/external/rag/datastores" \
  -H "X-API-Key: jns_live_YOUR_API_KEY_HERE"
Python
import requests

response = requests.get(
    "https://api.junis.ai/api/external/rag/datastores",
    headers={"X-API-Key": "jns_live_YOUR_API_KEY_HERE"}
)
print(response.json())

Response

Status Code: 200 OK
{
  "datastores": [
    {
      "datastore_id": "company-policies-a1b2c3d4",
      "display_name": "Company Policies",
      "description": "HR policies and employee handbook",
      "document_count": 5,
      "created_at": "2026-01-01T10:00:00Z",
      "is_active": true
    },
    {
      "datastore_id": "product-docs-e5f6g7h8",
      "display_name": "Product Documentation",
      "description": null,
      "document_count": 12,
      "created_at": "2026-01-01T08:30:00Z",
      "is_active": true
    }
  ],
  "total": 2
}

Response Fields

FieldTypeDescription
datastoresarrayList of DataStores
datastores[].datastore_idstringUnique DataStore ID
datastores[].display_namestringHuman-readable name
datastores[].descriptionstringDataStore description (null if not set)
datastores[].document_countintegerNumber of completed uploads
datastores[].created_atstringCreation timestamp (ISO 8601)
datastores[].is_activebooleanWhether DataStore is active
totalintegerTotal number of DataStores

List Uploads

Retrieve a paginated list of file uploads for your organization.

Endpoint

GET https://api.junis.ai/api/external/rag/uploads

Query Parameters

ParameterTypeRequiredDescription
limitintegerNoMaximum items to return (default: 50, max: 100)
offsetintegerNoNumber of items to skip (default: 0)
statusstringNoFilter by status: pending, processing, indexing, completed, failed
datastore_idstringNoFilter by DataStore ID

Request Example

cURL
curl -X GET "https://api.junis.ai/api/external/rag/uploads?limit=10&status=completed" \
  -H "X-API-Key: jns_live_YOUR_API_KEY_HERE"
Python
import requests

response = requests.get(
    "https://api.junis.ai/api/external/rag/uploads",
    headers={"X-API-Key": "jns_live_YOUR_API_KEY_HERE"},
    params={
        "limit": 10,
        "status": "completed"
    }
)
print(response.json())

Response

Status Code: 200 OK
{
  "uploads": [
    {
      "upload_id": "550e8400-e29b-41d4-a716-446655440000",
      "filename": "policy.pdf",
      "file_size": 102400,
      "file_type": "application/pdf",
      "datastore_id": "company-policies-a1b2c3d4",
      "datastore_name": "Company Policies",
      "status": "completed",
      "uploaded_at": "2026-01-01T10:00:00Z",
      "indexed_at": "2026-01-01T10:05:00Z",
      "error_message": null
    }
  ],
  "total": 42,
  "limit": 10,
  "offset": 0
}

Upload Status Values

StatusDescription
pendingUpload received, waiting for processing
processingFile being uploaded to GCS
indexingFile being indexed in Vertex AI Search
completedIndexing complete, document is searchable
failedUpload or indexing failed

Response Fields

FieldTypeDescription
uploadsarrayList of uploads
uploads[].upload_idstringUnique upload ID (UUID)
uploads[].filenamestringOriginal filename
uploads[].file_sizeintegerFile size in bytes
uploads[].file_typestringMIME type
uploads[].datastore_idstringTarget DataStore ID
uploads[].datastore_namestringTarget DataStore name
uploads[].statusstringCurrent status
uploads[].uploaded_atstringUpload timestamp (ISO 8601)
uploads[].indexed_atstringIndexing completion timestamp (null if not indexed)
uploads[].error_messagestringError description if failed
totalintegerTotal matching uploads
limitintegerItems per page
offsetintegerCurrent offset

Get Upload Status

Retrieve detailed status of a specific upload.

Endpoint

GET https://api.junis.ai/api/external/rag/uploads/{upload_id}

Path Parameters

ParameterTypeRequiredDescription
upload_idstringYesUpload ID (UUID format)

Request Example

cURL
curl -X GET "https://api.junis.ai/api/external/rag/uploads/550e8400-e29b-41d4-a716-446655440000" \
  -H "X-API-Key: jns_live_YOUR_API_KEY_HERE"

Response

Status Code: 200 OK
{
  "upload_id": "550e8400-e29b-41d4-a716-446655440000",
  "filename": "policy.pdf",
  "file_size": 102400,
  "file_type": "application/pdf",
  "status": "completed",
  "ready": true,
  "datastore_id": "company-policies-a1b2c3d4",
  "datastore_name": "Company Policies",
  "uploaded_at": "2026-01-01T10:00:00Z",
  "indexed_at": "2026-01-01T10:05:00Z",
  "error_message": null
}

Response Fields

FieldTypeDescription
upload_idstringUnique upload ID (UUID)
filenamestringOriginal filename
file_sizeintegerFile size in bytes
file_typestringMIME type
statusstringCurrent status
readybooleantrue if document is searchable (status === completed)
datastore_idstringTarget DataStore ID
datastore_namestringTarget DataStore name
uploaded_atstringUpload timestamp (ISO 8601)
indexed_atstringIndexing completion timestamp (null if not indexed)
error_messagestringError description if failed

Error Responses

404 Not Found
{
  "detail": {
    "error": {
      "message": "Upload not found: 550e8400-e29b-41d4-a716-446655440000",
      "type": "not_found",
      "code": "upload_not_found"
    }
  }
}

List DataStore Files

Retrieve all files in a specific DataStore with their indexing status.

Endpoint

GET https://api.junis.ai/api/external/rag/datastores/{datastore_id}/files

Path Parameters

ParameterTypeRequiredDescription
datastore_idstringYesDataStore ID

Request Example

cURL
curl -X GET "https://api.junis.ai/api/external/rag/datastores/company-policies-a1b2c3d4/files" \
  -H "X-API-Key: jns_live_YOUR_API_KEY_HERE"
Python
import requests

response = requests.get(
    "https://api.junis.ai/api/external/rag/datastores/company-policies-a1b2c3d4/files",
    headers={"X-API-Key": "jns_live_YOUR_API_KEY_HERE"}
)
print(response.json())
JavaScript
const response = await fetch(
  'https://api.junis.ai/api/external/rag/datastores/company-policies-a1b2c3d4/files',
  {
    headers: { 'X-API-Key': 'jns_live_YOUR_API_KEY_HERE' }
  }
);
const data = await response.json();
console.log(data);

Response

Status Code: 200 OK
{
  "datastore_id": "company-policies-a1b2c3d4",
  "datastore_name": "Company Policies",
  "files": [
    {
      "name": "employee-handbook.pdf",
      "size": 2048576,
      "content_type": "application/pdf",
      "uploaded_at": "2026-01-01T10:00:00Z",
      "status": "completed"
    },
    {
      "name": "vacation-policy.docx",
      "size": 102400,
      "content_type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
      "uploaded_at": "2026-01-01T10:05:00Z",
      "status": "indexing"
    }
  ],
  "total_files": 2,
  "indexed_files": 1
}

Response Fields

FieldTypeDescription
datastore_idstringDataStore ID
datastore_namestringDataStore display name
filesarrayList of files in the DataStore
files[].namestringFilename
files[].sizeintegerFile size in bytes
files[].content_typestringMIME type
files[].uploaded_atstringUpload timestamp (ISO 8601)
files[].statusstringIndexing status (pending, indexing, completed, failed)
total_filesintegerTotal number of files
indexed_filesintegerNumber of fully indexed files

Error Responses

404 Not Found
{
  "detail": {
    "error": {
      "message": "DataStore not found: invalid-datastore-id",
      "type": "not_found",
      "code": "datastore_not_found"
    }
  }
}

Delete DataStore

Delete a DataStore and all its contents (files and indexed documents).

Endpoint

DELETE https://api.junis.ai/api/external/rag/datastores/{datastore_id}
Destructive Operation: This permanently deletes all files and indexed documents in the DataStore. This action cannot be undone.

Path Parameters

ParameterTypeRequiredDescription
datastore_idstringYesDataStore ID to delete

Query Parameters

ParameterTypeRequiredDescription
forcebooleanNoIf true, delete even if DataStore is connected to agents (default: false)

Request Example

cURL
curl -X DELETE "https://api.junis.ai/api/external/rag/datastores/company-policies-a1b2c3d4?force=true" \
  -H "X-API-Key: jns_live_YOUR_API_KEY_HERE"
Python
import requests

response = requests.delete(
    "https://api.junis.ai/api/external/rag/datastores/company-policies-a1b2c3d4",
    headers={"X-API-Key": "jns_live_YOUR_API_KEY_HERE"},
    params={"force": True}
)
print(response.json())
JavaScript
const response = await fetch(
  'https://api.junis.ai/api/external/rag/datastores/company-policies-a1b2c3d4?force=true',
  {
    method: 'DELETE',
    headers: { 'X-API-Key': 'jns_live_YOUR_API_KEY_HERE' }
  }
);
const data = await response.json();
console.log(data);

Response

Status Code: 200 OK
{
  "success": true,
  "datastore_id": "company-policies-a1b2c3d4",
  "message": "DataStore 'Company Policies' deleted successfully"
}

Response Fields

FieldTypeDescription
successbooleanWhether deletion was successful
datastore_idstringDeleted DataStore ID
messagestringSuccess message

Error Responses

404 Not Found
{
  "detail": {
    "error": {
      "message": "DataStore not found: invalid-datastore-id",
      "type": "not_found",
      "code": "datastore_not_found"
    }
  }
}
409 Conflict - DataStore In Use
{
  "detail": {
    "error": {
      "message": "DataStore is connected to agents. Use force=true to delete anyway.",
      "type": "conflict",
      "code": "datastore_in_use",
      "connected_agents": ["brand-analyzer", "content-writer"]
    }
  }
}

Delete File

Delete a single file from a DataStore while preserving the DataStore itself.

Endpoint

DELETE https://api.junis.ai/api/external/rag/datastores/{datastore_id}/files/{filename}
Partial Deletion: Only the specified file is removed. The DataStore and other files remain intact.

Path Parameters

ParameterTypeRequiredDescription
datastore_idstringYesDataStore ID
filenamestringYesName of the file to delete (URL-encoded if contains special characters)

Request Example

cURL
curl -X DELETE "https://api.junis.ai/api/external/rag/datastores/company-policies-a1b2c3d4/files/old-policy.pdf" \
  -H "X-API-Key: jns_live_YOUR_API_KEY_HERE"
Python
import requests

response = requests.delete(
    "https://api.junis.ai/api/external/rag/datastores/company-policies-a1b2c3d4/files/old-policy.pdf",
    headers={"X-API-Key": "jns_live_YOUR_API_KEY_HERE"}
)
print(response.json())
JavaScript
const response = await fetch(
  'https://api.junis.ai/api/external/rag/datastores/company-policies-a1b2c3d4/files/old-policy.pdf',
  {
    method: 'DELETE',
    headers: { 'X-API-Key': 'jns_live_YOUR_API_KEY_HERE' }
  }
);
const data = await response.json();
console.log(data);

Response

Status Code: 200 OK
{
  "success": true,
  "datastore_id": "company-policies-a1b2c3d4",
  "filename": "old-policy.pdf",
  "remaining_files": 4,
  "message": "File 'old-policy.pdf' deleted from 'Company Policies'"
}

Response Fields

FieldTypeDescription
successbooleanWhether deletion was successful
datastore_idstringDataStore ID
filenamestringDeleted filename
remaining_filesintegerNumber of files remaining in the DataStore
messagestringSuccess message

Error Responses

404 Not Found - DataStore
{
  "detail": {
    "error": {
      "message": "DataStore not found: invalid-datastore-id",
      "type": "not_found",
      "code": "datastore_not_found"
    }
  }
}
500 Internal Server Error - File Not Found
{
  "detail": {
    "error": {
      "message": "Failed to delete file: File not found in DataStore",
      "type": "deletion_error",
      "code": "file_deletion_failed"
    }
  }
}

Best Practices

When an upload is in processing or indexing status, poll the status endpoint to check for completion:
import time
import requests

def wait_for_upload(upload_id, api_key, timeout=300, interval=5):
    """Poll until upload is completed or failed."""
    start = time.time()
    while time.time() - start < timeout:
        response = requests.get(
            f"https://api.junis.ai/api/external/rag/uploads/{upload_id}",
            headers={"X-API-Key": api_key}
        )
        data = response.json()

        if data["ready"]:
            return data
        elif data["status"] == "failed":
            raise Exception(f"Upload failed: {data.get('error_message')}")

        time.sleep(interval)

    raise TimeoutError("Upload indexing timed out")
Upload multiple related files in a single request (up to 10 files):
import base64
import requests
import os

def upload_directory(datastore_id, directory_path, api_key):
    """Upload all supported files from a directory."""
    files = []
    for filename in os.listdir(directory_path):
        filepath = os.path.join(directory_path, filename)
        if os.path.isfile(filepath):
            with open(filepath, "rb") as f:
                content = base64.b64encode(f.read()).decode()
            files.append({
                "filename": filename,
                "content": content
            })

    response = requests.post(
        "https://api.junis.ai/api/external/rag/upload",
        headers={"X-API-Key": api_key, "Content-Type": "application/json"},
        json={"datastore_id": datastore_id, "files": files[:10]}  # Max 10
    )
    return response.json()
Create separate DataStores for different topics or document types:
  • By Department: “HR Policies”, “Engineering Docs”, “Sales Materials”
  • By Project: “Project Alpha”, “Project Beta”
  • By Type: “Legal Contracts”, “Technical Specs”, “Meeting Notes”
This helps agents retrieve more relevant information by searching specific DataStores.
For large documents:
  • Split large PDFs into smaller chapters
  • Remove unnecessary images from documents
  • Use text formats (TXT, MD) when formatting isn’t important
  • Compress images before including in documents