# Branches Source: https://docs.hellocobi.com/api-reference/data/endpoint/branches POST /v1/data/branches Upload branch records to the Cobi Data API. **Important**: Branch records have a dependency on shops. The system validates the `shop_id` field against existing shops in the database. You should upload shop records before uploading branch records to ensure proper data validation. ## Authentication Required This endpoint requires API key authentication via Bearer token in the Authorization header. ```http theme={null} Authorization: Bearer YOUR_API_KEY Content-Type: application/json ``` ## About the Data API This endpoint is part of the Cobi Data API (`/v1/data/:type`), which provides a consistent interface for uploading different types of structured data. ## Request Body An array of branch records (up to 5000) Unique identifier for the branch ID of the shop this branch belongs to (must reference an existing shop) Name of the branch Geographic region (2-letter country code, e.g., "CA" for Canada) Latitude coordinate of the branch location Longitude coordinate of the branch location Timestamp when the record was created Timestamp when the record was last updated Flag indicating if the record is marked as deleted ## Response The API returns a JSON response with the following fields: ```json theme={null} { "request_id": "string", // Unique ID for the overall request "type": "branches", // Type of data being processed "batches": [ { "batch": 1, // Sequential batch number "status": 200, // HTTP-like status code (200, 207, 400, 500) "data": { "status": "success", // "success", "partial", or "error" "type": "branches", // Matches the top-level type "message": "Records uploaded successfully", // Summary message "request_id": "string", "batch_id": "string", "error": "" // Optional — empty or null on success; contains error details otherwise } } ] } ``` | Field | Type | Description | | --------------------------- | ------ | ----------------------------------------------- | | `request_id` | string | Unique ID for the overall request | | `type` | string | Type of data being processed (e.g., "branches") | | `batches` | array | Array of batch results | | `batches[].batch` | int | Sequential batch number | | `batches[].status` | int | HTTP-like status code (200, 207, 400, 500) | | `batches[].data` | object | Batch result details | | `batches[].data.status` | string | "success", "partial", or "error" | | `batches[].data.type` | string | Matches the top-level type | | `batches[].data.message` | string | Summary message of the batch result | | `batches[].data.request_id` | string | Unique request ID for the batch | | `batches[].data.batch_id` | string | Unique batch ID | | `batches[].data.error` | string | Error details (empty or null on success) | ## Error Codes | Status Code | Description | data.status value | | ----------- | ----------------------------------------- | ----------------- | | 200 | All records processed successfully | success | | 207 | Some records failed, others succeeded | partial | | 400 | Validation or dependency error | error | | 500 | Internal or unexpected server error | error | | 401 | Unauthorized - Invalid or missing API key | error | ## Example ```bash theme={null} curl -X POST https://api.hellocobi.com/v1/data/branches \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "records": [ { "branch_id": "550e8400-e29b-41d4-a716-446655440000", "shop_id": "550e8400-e29b-41d4-a716-446655440001", "name": "Downtown Branch", "region": "CA", "latitude": 43.651070, "longitude": -79.347015, "created": "2023-01-15T14:30:00Z", "updated": "2023-06-22T09:15:30Z", "is_deleted": false } ] }' ``` ## Best Practices 1. **Upload Order**: Upload shops before branches to ensure shop\_id references exist 2. **Batch Size**: Upload up to 5000 records at a time for optimal performance 3. **Data Validation**: Ensure all required fields are present and properly formatted 4. **Error Handling**: Implement proper error handling for failed requests 5. **Rate Limiting**: Be mindful of API rate limits Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Categories Source: https://docs.hellocobi.com/api-reference/data/endpoint/categories POST /v1/data/categories Upload category records to the Cobi Data API. ## Authentication Required This endpoint requires API key authentication via Bearer token in the Authorization header. ```http theme={null} Authorization: Bearer YOUR_API_KEY Content-Type: application/json ``` ## About the Data API This endpoint is part of the Cobi Data API (`/v1/data/:type`), which provides a consistent interface for uploading different types of structured data. ## Request Body An array of category records (up to 5000) Unique identifier for the category Name of the category Geographic region (2-letter country code, e.g., "CA" for Canada) Timestamp when the record was created Timestamp when the record was last updated Flag indicating if the record is marked as deleted ## Response The API returns a JSON response with the following fields: ```json theme={null} { "request_id": "string", // Unique ID for the overall request "type": "categories", // Type of data being processed "batches": [ { "batch": 1, // Sequential batch number "status": 200, // HTTP-like status code (200, 207, 400, 500) "data": { "status": "success", // "success", "partial", or "error" "type": "categories", // Matches the top-level type "message": "Records uploaded successfully", // Summary message "request_id": "string", "batch_id": "string", "error": "" // Optional — empty or null on success; contains error details otherwise } } ] } ``` | Field | Type | Description | | --------------------------- | ------ | ------------------------------------------------- | | `request_id` | string | Unique ID for the overall request | | `type` | string | Type of data being processed (e.g., "categories") | | `batches` | array | Array of batch results | | `batches[].batch` | int | Sequential batch number | | `batches[].status` | int | HTTP-like status code (200, 207, 400, 500) | | `batches[].data` | object | Batch result details | | `batches[].data.status` | string | "success", "partial", or "error" | | `batches[].data.type` | string | Matches the top-level type | | `batches[].data.message` | string | Summary message of the batch result | | `batches[].data.request_id` | string | Unique request ID for the batch | | `batches[].data.batch_id` | string | Unique batch ID | | `batches[].data.error` | string | Error details (empty or null on success) | ## Error Codes | Status Code | Description | data.status value | | ----------- | ----------------------------------------- | ----------------- | | 200 | All records processed successfully | success | | 207 | Some records failed, others succeeded | partial | | 400 | Validation or dependency error | error | | 500 | Internal or unexpected server error | error | | 401 | Unauthorized - Invalid or missing API key | error | ## Example ```bash theme={null} curl -X POST https://api.hellocobi.com/v1/data/categories \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "records": [ { "category_id": "550e8400-e29b-41d4-a716-446655440000", "name": "Electronics", "region": "CA", "created": "2023-01-15T14:30:00Z", "updated": "2023-06-22T09:15:30Z", "is_deleted": false } ] }' ``` ## Best Practices 1. **Batch Size**: Upload up to 5000 records at a time for optimal performance 2. **Data Validation**: Ensure all required fields are present and properly formatted 3. **Error Handling**: Implement proper error handling for failed requests 4. **Rate Limiting**: Be mindful of API rate limits Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Courses Source: https://docs.hellocobi.com/api-reference/data/endpoint/courses POST /v1/data/courses Upload course records to the Cobi Data API. ## Authentication Required This endpoint requires API key authentication via Bearer token in the Authorization header. ```http theme={null} Authorization: Bearer YOUR_API_KEY Content-Type: application/json ``` ## About the Data API This endpoint is part of the Cobi Data API (`/v1/data/:type`), which provides a consistent interface for uploading different types of structured data. ## Request Body An array of course records (up to 1000) Unique identifier for the course Name of the course Timestamp when the record was created Timestamp when the record was last updated Flag indicating if the record is marked as deleted ## Response The API returns a JSON response with the following fields: ```json theme={null} { "request_id": "string", "type": "courses", "batches": [ { "batch": 1, "status": 200, "data": { "status": "success", "type": "courses", "message": "Records uploaded successfully", "request_id": "string", "batch_id": "string", "error": "" } } ] } ``` | Field | Type | Description | | --------------------------- | ------ | ---------------------------------------------- | | `request_id` | string | Unique ID for the overall request | | `type` | string | Type of data being processed (e.g., "courses") | | `batches` | array | Array of batch results | | `batches[].batch` | int | Sequential batch number | | `batches[].status` | int | HTTP-like status code (200, 207, 400, 500) | | `batches[].data` | object | Batch result details | | `batches[].data.status` | string | "success", "partial", or "error" | | `batches[].data.type` | string | Matches the top-level type | | `batches[].data.message` | string | Summary message of the batch result | | `batches[].data.request_id` | string | Unique request ID for the batch | | `batches[].data.batch_id` | string | Unique batch ID | | `batches[].data.error` | string | Error details (empty or null on success) | ## Error Codes | Status Code | Description | data.status value | | ----------- | ----------------------------------------- | ----------------- | | 200 | All records processed successfully | success | | 207 | Some records failed, others succeeded | partial | | 400 | Validation or dependency error | error | | 500 | Internal or unexpected server error | error | | 401 | Unauthorized - Invalid or missing API key | error | ## Example ```bash theme={null} curl -X POST https://api.hellocobi.com/v1/data/courses \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "records": [ { "course_id": "550e8400-e29b-41d4-a716-446655440000", "name": "Introduction to Computer Science", "created": "2023-01-15T14:30:00Z", "updated": "2023-06-22T09:15:30Z", "is_deleted": false } ] }' ``` ## Best Practices 1. **Batch Size**: Upload up to 5000 records at a time for optimal performance 2. **Data Validation**: Ensure all required fields are present and properly formatted 3. **Error Handling**: Implement proper error handling for failed requests 4. **Rate Limiting**: Be mindful of API rate limits Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Customers Source: https://docs.hellocobi.com/api-reference/data/endpoint/customers POST /v1/data/customers Upload customer records to the Cobi Data API. ## Authentication Required This endpoint requires API key authentication via Bearer token in the Authorization header. ```http theme={null} Authorization: Bearer YOUR_API_KEY Content-Type: application/json ``` ## About the Data API This endpoint is part of the Cobi Data API (`/v1/data/:type`), which provides a consistent interface for uploading different types of structured data. ## Request Body An array of customer records (up to 5000) Unique identifier for the customer Unique identifier for the user Geographic region (2-letter country code, e.g., "CA" for Canada) Customer's date of birth ID of the educational institute ID of the course Customer's gender Timestamp when the record was created Timestamp when the record was last updated Flag indicating if the record is marked as deleted ## Response The API returns a JSON response with the following fields: ```json theme={null} { "request_id": "string", // Unique ID for the overall request "type": "customers", // Type of data being processed "batches": [ { "batch": 1, // Sequential batch number "status": 200, // HTTP-like status code (200, 207, 400, 500) "data": { "status": "success", // "success", "partial", or "error" "type": "customers", // Matches the top-level type "message": "Records uploaded successfully", // Summary message "request_id": "string", "batch_id": "string", "error": "" // Optional — empty or null on success; contains error details otherwise } } ] } ``` | Field | Type | Description | | --------------------------- | ------ | ------------------------------------------------ | | `request_id` | string | Unique ID for the overall request | | `type` | string | Type of data being processed (e.g., "customers") | | `batches` | array | Array of batch results | | `batches[].batch` | int | Sequential batch number | | `batches[].status` | int | HTTP-like status code (200, 207, 400, 500) | | `batches[].data` | object | Batch result details | | `batches[].data.status` | string | "success", "partial", or "error" | | `batches[].data.type` | string | Matches the top-level type | | `batches[].data.message` | string | Summary message of the batch result | | `batches[].data.request_id` | string | Unique request ID for the batch | | `batches[].data.batch_id` | string | Unique batch ID | | `batches[].data.error` | string | Error details (empty or null on success) | ## Error Codes | Status Code | Description | data.status value | | ----------- | ----------------------------------------- | ----------------- | | 200 | All records processed successfully | success | | 207 | Some records failed, others succeeded | partial | | 400 | Validation or dependency error | error | | 500 | Internal or unexpected server error | error | | 401 | Unauthorized - Invalid or missing API key | error | ## Example ```bash theme={null} curl -X POST https://api.hellocobi.com/v1/data/customers \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "records": [ { "customer_id": "550e8400-e29b-41d4-a716-446655440000", "user_id": "550e8400-e29b-41d4-a716-446655440001", "region": "CA", "birthdate": "1990-01-01", "gender": "MALE", "created": "2023-01-15T14:30:00Z", "updated": "2023-06-22T09:15:30Z", "is_deleted": false } ] }' ``` ## Best Practices 1. **Batch Size**: Upload up to 5000 records at a time for optimal performance 2. **Data Validation**: Ensure all required fields are present and properly formatted 3. **Error Handling**: Implement proper error handling for failed requests 4. **Rate Limiting**: Be mindful of API rate limits Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Institutes Source: https://docs.hellocobi.com/api-reference/data/endpoint/institutes POST /v1/data/institutes Upload institute records to the Cobi Data API. ## Authentication Required This endpoint requires API key authentication via Bearer token in the Authorization header. ```http theme={null} Authorization: Bearer YOUR_API_KEY Content-Type: application/json ``` ## About the Data API This endpoint is part of the Cobi Data API (`/v1/data/:type`), which provides a consistent interface for uploading different types of structured data. ## Request Body An array of institute records (up to 5000) Unique identifier for the institute Name of the institute Geographic region (2-letter country code, e.g., "CA" for Canada) Timestamp when the record was created Timestamp when the record was last updated Flag indicating if the record is marked as deleted ## Response The API returns a JSON response with the following fields: ```json theme={null} { "request_id": "string", // Unique ID for the overall request "type": "institutes", // Type of data being processed "batches": [ { "batch": 1, // Sequential batch number "status": 200, // HTTP-like status code (200, 207, 400, 500) "data": { "status": "success", // "success", "partial", or "error" "type": "institutes", // Matches the top-level type "message": "Records uploaded successfully", // Summary message "request_id": "string", "batch_id": "string", "error": "" // Optional — empty or null on success; contains error details otherwise } } ] } ``` | Field | Type | Description | | --------------------------- | ------ | ------------------------------------------------- | | `request_id` | string | Unique ID for the overall request | | `type` | string | Type of data being processed (e.g., "institutes") | | `batches` | array | Array of batch results | | `batches[].batch` | int | Sequential batch number | | `batches[].status` | int | HTTP-like status code (200, 207, 400, 500) | | `batches[].data` | object | Batch result details | | `batches[].data.status` | string | "success", "partial", or "error" | | `batches[].data.type` | string | Matches the top-level type | | `batches[].data.message` | string | Summary message of the batch result | | `batches[].data.request_id` | string | Unique request ID for the batch | | `batches[].data.batch_id` | string | Unique batch ID | | `batches[].data.error` | string | Error details (empty or null on success) | ## Error Codes | Status Code | Description | data.status value | | ----------- | ----------------------------------------- | ----------------- | | 200 | All records processed successfully | success | | 207 | Some records failed, others succeeded | partial | | 400 | Validation or dependency error | error | | 500 | Internal or unexpected server error | error | | 401 | Unauthorized - Invalid or missing API key | error | ## Example ```bash theme={null} curl -X POST https://api.hellocobi.com/v1/data/institutes \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "records": [ { "institute_id": "550e8400-e29b-41d4-a716-446655440000", "name": "University of Toronto", "region": "CA", "created": "2023-01-15T14:30:00Z", "updated": "2023-06-22T09:15:30Z", "is_deleted": false } ] }' ``` ## Best Practices 1. **Batch Size**: Upload up to 5000 records at a time for optimal performance 2. **Data Validation**: Ensure all required fields are present and properly formatted 3. **Error Handling**: Implement proper error handling for failed requests 4. **Rate Limiting**: Be mindful of API rate limits Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Keywords Source: https://docs.hellocobi.com/api-reference/data/endpoint/keywords POST /v1/data/keywords Upload keyword records to the Cobi Data API. ## Authentication Required This endpoint requires API key authentication via Bearer token in the Authorization header. ```http theme={null} Authorization: Bearer YOUR_API_KEY Content-Type: application/json ``` ## About the Data API This endpoint is part of the Cobi Data API (`/v1/data/:type`), which provides a consistent interface for uploading different types of structured data. ## Request Body An array of keyword records (up to 5000) Unique identifier for the keyword Name of the keyword Timestamp when the record was created Timestamp when the record was last updated Flag indicating if the record is marked as deleted ## Response The API returns a JSON response with the following fields: ```json theme={null} { "request_id": "string", // Unique ID for the overall request "type": "keywords", // Type of data being processed "batches": [ { "batch": 1, // Sequential batch number "status": 200, // HTTP-like status code (200, 207, 400, 500) "data": { "status": "success", // "success", "partial", or "error" "type": "keywords", // Matches the top-level type "message": "Records uploaded successfully", // Summary message "request_id": "string", "batch_id": "string", "error": "" // Optional — empty or null on success; contains error details otherwise } } ] } ``` | Field | Type | Description | | --------------------------- | ------ | ----------------------------------------------- | | `request_id` | string | Unique ID for the overall request | | `type` | string | Type of data being processed (e.g., "keywords") | | `batches` | array | Array of batch results | | `batches[].batch` | int | Sequential batch number | | `batches[].status` | int | HTTP-like status code (200, 207, 400, 500) | | `batches[].data` | object | Batch result details | | `batches[].data.status` | string | "success", "partial", or "error" | | `batches[].data.type` | string | Matches the top-level type | | `batches[].data.message` | string | Summary message of the batch result | | `batches[].data.request_id` | string | Unique request ID for the batch | | `batches[].data.batch_id` | string | Unique batch ID | | `batches[].data.error` | string | Error details (empty or null on success) | ## Error Codes | Status Code | Description | data.status value | | ----------- | ----------------------------------------- | ----------------- | | 200 | All records processed successfully | success | | 207 | Some records failed, others succeeded | partial | | 400 | Validation or dependency error | error | | 500 | Internal or unexpected server error | error | | 401 | Unauthorized - Invalid or missing API key | error | ## Example ```bash theme={null} curl -X POST https://api.hellocobi.com/v1/data/keywords \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "records": [ { "keyword_id": "550e8400-e29b-41d4-a716-446655440000", "name": "Student Discount", "created": "2023-01-15T14:30:00Z", "updated": "2023-06-22T09:15:30Z", "is_deleted": false } ] }' ``` ## Best Practices 1. **Batch Size**: Upload up to 5000 records at a time for optimal performance 2. **Data Validation**: Ensure all required fields are present and properly formatted 3. **Error Handling**: Implement proper error handling for failed requests 4. **Rate Limiting**: Be mindful of API rate limits Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Offers Source: https://docs.hellocobi.com/api-reference/data/endpoint/offers POST /v1/data/offers Upload offer records to the Cobi Data API **Important**: Offer records have a dependency on shops. The system validates the `shop_id` field against existing records in the database. You should upload shops before uploading offers to ensure proper data validation. ## Authentication Required This endpoint requires API key authentication via Bearer token in the Authorization header. ```http theme={null} Authorization: Bearer YOUR_API_KEY Content-Type: application/json ``` ## About the Data API This endpoint is part of the Cobi Data API (`/v1/data/:type`), which provides a consistent interface for uploading different types of structured data. ## Request Body An array of offer records (up to 5000) Unique identifier for the offer ID of the shop offering the discount (must reference an existing shop) Name of the offer Type of offer (e.g., "percentage", "fixed") Value of the offer (must be a positive number) Geographic region (2-letter country code, e.g., "CA" for Canada) Start date and time of the offer End date and time of the offer Comma-separated list of branch IDs where the offer is valid Comma-separated list of days when the offer is valid Timestamp when the record was created Timestamp when the record was last updated Flag indicating if the record is marked as deleted Flag indicating if the offer is currently enabled ## Response The API returns a JSON response with the following fields: ```json theme={null} { "request_id": "string", // Unique ID for the overall request "type": "offers", // Type of data being processed "batches": [ { "batch": 1, // Sequential batch number "status": 200, // HTTP-like status code (200, 207, 400, 500) "data": { "status": "success", // "success", "partial", or "error" "type": "offers", // Matches the top-level type "message": "Records uploaded successfully", // Summary message "request_id": "string", "batch_id": "string", "error": "" // Optional — empty or null on success; contains error details otherwise } } ] } ``` | Field | Type | Description | | --------------------------- | ------ | --------------------------------------------- | | `request_id` | string | Unique ID for the overall request | | `type` | string | Type of data being processed (e.g., "offers") | | `batches` | array | Array of batch results | | `batches[].batch` | int | Sequential batch number | | `batches[].status` | int | HTTP-like status code (200, 207, 400, 500) | | `batches[].data` | object | Batch result details | | `batches[].data.status` | string | "success", "partial", or "error" | | `batches[].data.type` | string | Matches the top-level type | | `batches[].data.message` | string | Summary message of the batch result | | `batches[].data.request_id` | string | Unique request ID for the batch | | `batches[].data.batch_id` | string | Unique batch ID | | `batches[].data.error` | string | Error details (empty or null on success) | ## Error Codes | Status Code | Description | data.status value | | ----------- | ----------------------------------------- | ----------------- | | 200 | All records processed successfully | success | | 207 | Some records failed, others succeeded | partial | | 400 | Validation or dependency error | error | | 500 | Internal or unexpected server error | error | | 401 | Unauthorized - Invalid or missing API key | error | ## Example ```bash theme={null} curl -X POST https://api.hellocobi.com/v1/data/offers \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "records": [ { "offer_id": "550e8400-e29b-41d4-a716-446655440000", "shop_id": "550e8400-e29b-41d4-a716-446655440001", "name": "Student Discount", "type": "percentage", "value": 10.0, "region": "CA", "allowed_from": "2024-01-01T00:00:00Z", "allowed_to": "2024-12-31T23:59:59Z", "allowed_branches": "branch1,branch2", "allowed_days": "monday,tuesday,wednesday", "is_enabled": true } ] }' ``` ## Best Practices 1. **Upload Order**: Upload shops before offers to ensure references exist 2. **Batch Size**: Upload up to 5000 records at a time for optimal performance 3. **Data Validation**: Ensure all required fields are present and properly formatted 4. **Error Handling**: Implement proper error handling for failed requests 5. **Rate Limiting**: Be mindful of API rate limits Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Shop Categories Source: https://docs.hellocobi.com/api-reference/data/endpoint/shop-categories POST /v1/data/shop-categories Upload shop category mappings to the Cobi Data API **Important**: Shop category records have dependencies on both shops and categories. The system validates both the `shop_id` and `category_id` fields against existing records in the database. You should upload shops and categories before uploading shop category mappings to ensure proper data validation. ## Authentication Required This endpoint requires API key authentication via Bearer token in the Authorization header. ```http theme={null} Authorization: Bearer YOUR_API_KEY Content-Type: application/json ``` ## About the Data API This endpoint is part of the Cobi Data API (`/v1/data/:type`), which provides a consistent interface for uploading different types of structured data. ## Request Body An array of shop category mapping records (up to 5000) Unique identifier for the shop category mapping ID of the shop (must reference an existing shop) ID of the category (must reference an existing category) Geographic region (2-letter country code, e.g., "CA" for Canada) Timestamp when the record was created Timestamp when the record was last updated Flag indicating if the record is marked as deleted ## Response The API returns a JSON response with the following fields: ```json theme={null} { "request_id": "string", // Unique ID for the overall request "type": "shop-categories", // Type of data being processed "batches": [ { "batch": 1, // Sequential batch number "status": 200, // HTTP-like status code (200, 207, 400, 500) "data": { "status": "success", // "success", "partial", or "error" "type": "shop-categories", // Matches the top-level type "message": "Records uploaded successfully", // Summary message "request_id": "string", "batch_id": "string", "error": "" // Optional — empty or null on success; contains error details otherwise } } ] } ``` | Field | Type | Description | | --------------------------- | ------ | ------------------------------------------------------ | | `request_id` | string | Unique ID for the overall request | | `type` | string | Type of data being processed (e.g., "shop-categories") | | `batches` | array | Array of batch results | | `batches[].batch` | int | Sequential batch number | | `batches[].status` | int | HTTP-like status code (200, 207, 400, 500) | | `batches[].data` | object | Batch result details | | `batches[].data.status` | string | "success", "partial", or "error" | | `batches[].data.type` | string | Matches the top-level type | | `batches[].data.message` | string | Summary message of the batch result | | `batches[].data.request_id` | string | Unique request ID for the batch | | `batches[].data.batch_id` | string | Unique batch ID | | `batches[].data.error` | string | Error details (empty or null on success) | ## Error Codes | Status Code | Description | data.status value | | ----------- | ----------------------------------------- | ----------------- | | 200 | All records processed successfully | success | | 207 | Some records failed, others succeeded | partial | | 400 | Validation or dependency error | error | | 500 | Internal or unexpected server error | error | | 401 | Unauthorized - Invalid or missing API key | error | ## Example ```bash theme={null} curl -X POST https://api.hellocobi.com/v1/data/shop-categories \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "records": [ { "shop_category_id": "550e8400-e29b-41d4-a716-446655440000", "shop_id": "550e8400-e29b-41d4-a716-446655440001", "category_id": "550e8400-e29b-41d4-a716-446655440002", "region": "CA", "created": "2023-01-15T14:30:00Z", "updated": "2023-06-22T09:15:30Z", "is_deleted": false } ] }' ``` ## Best Practices 1. **Upload Order**: Upload shops and categories before shop-categories to ensure references exist 2. **Batch Size**: Upload up to 5000 records at a time for optimal performance 3. **Data Validation**: Ensure all required fields are present and properly formatted 4. **Error Handling**: Implement proper error handling for failed requests 5. **Rate Limiting**: Be mindful of API rate limits Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Shop Keywords Source: https://docs.hellocobi.com/api-reference/data/endpoint/shop-keywords POST /v1/data/shop-keywords Upload shop keyword mappings to the Cobi Data API **Important**: Shop keyword records have dependencies on both shops and keywords. The system validates both the `shop_id` and `keyword_id` fields against existing records in the database. You should upload shops and keywords before uploading shop keyword mappings to ensure proper data validation. ## Authentication Required This endpoint requires API key authentication via Bearer token in the Authorization header. ```http theme={null} Authorization: Bearer YOUR_API_KEY Content-Type: application/json ``` ## About the Data API This endpoint is part of the Cobi Data API (`/v1/data/:type`), which provides a consistent interface for uploading different types of structured data. ## Request Body An array of shop keyword mapping records (up to 5000) Unique identifier for the shop keyword mapping ID of the shop (must reference an existing shop) ID of the keyword (must reference an existing keyword) Timestamp when the record was created Timestamp when the record was last updated Flag indicating if the record is marked as deleted ## Response The API returns a JSON response with the following fields: ```json theme={null} { "request_id": "string", // Unique ID for the overall request "type": "shop-keywords", // Type of data being processed "batches": [ { "batch": 1, // Sequential batch number "status": 200, // HTTP-like status code (200, 207, 400, 500) "data": { "status": "success", // "success", "partial", or "error" "type": "shop-keywords", // Matches the top-level type "message": "Records uploaded successfully", // Summary message "request_id": "string", "batch_id": "string", "error": "" // Optional — empty or null on success; contains error details otherwise } } ] } ``` | Field | Type | Description | | --------------------------- | ------ | ---------------------------------------------------- | | `request_id` | string | Unique ID for the overall request | | `type` | string | Type of data being processed (e.g., "shop-keywords") | | `batches` | array | Array of batch results | | `batches[].batch` | int | Sequential batch number | | `batches[].status` | int | HTTP-like status code (200, 207, 400, 500) | | `batches[].data` | object | Batch result details | | `batches[].data.status` | string | "success", "partial", or "error" | | `batches[].data.type` | string | Matches the top-level type | | `batches[].data.message` | string | Summary message of the batch result | | `batches[].data.request_id` | string | Unique request ID for the batch | | `batches[].data.batch_id` | string | Unique batch ID | | `batches[].data.error` | string | Error details (empty or null on success) | ## Error Codes | Status Code | Description | data.status value | | ----------- | ----------------------------------------- | ----------------- | | 200 | All records processed successfully | success | | 207 | Some records failed, others succeeded | partial | | 400 | Validation or dependency error | error | | 500 | Internal or unexpected server error | error | | 401 | Unauthorized - Invalid or missing API key | error | ## Example ```bash theme={null} curl -X POST https://api.hellocobi.com/v1/data/shop-keywords \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "records": [ { "shop_keyword_id": "550e8400-e29b-41d4-a716-446655440000", "shop_id": "550e8400-e29b-41d4-a716-446655440001", "keyword_id": "550e8400-e29b-41d4-a716-446655440002", "created": "2023-01-15T14:30:00Z", "updated": "2023-06-22T09:15:30Z", "is_deleted": false } ] }' ``` ## Best Practices 1. **Upload Order**: Upload shops and keywords before shop-keywords to ensure references exist 2. **Batch Size**: Upload up to 5000 records at a time for optimal performance 3. **Data Validation**: Ensure all required fields are present and properly formatted 4. **Error Handling**: Implement proper error handling for failed requests 5. **Rate Limiting**: Be mindful of API rate limits Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Shop Tags Source: https://docs.hellocobi.com/api-reference/data/endpoint/shop-tags POST /v1/data/shop-tags Upload shop tag mappings to the Cobi Data API **Important**: Shop tag records have dependencies on both shops and tags. The system validates both the `shop_id` and `tag_id` fields against existing records in the database. You should upload shops and tags before uploading shop tag mappings to ensure proper data validation. ## Authentication Required This endpoint requires API key authentication via Bearer token in the Authorization header. ```http theme={null} Authorization: Bearer YOUR_API_KEY Content-Type: application/json ``` ## About the Data API This endpoint is part of the Cobi Data API (`/v1/data/:type`), which provides a consistent interface for uploading different types of structured data. ## Request Body An array of shop tag mapping records (up to 5000) Unique identifier for the shop tag mapping ID of the shop (must reference an existing shop) ID of the tag (must reference an existing tag) Timestamp when the record was created Timestamp when the record was last updated Flag indicating if the record is marked as deleted ## Response The API returns a JSON response with the following fields: ```json theme={null} { "request_id": "string", // Unique ID for the overall request "type": "shop-tags", // Type of data being processed "batches": [ { "batch": 1, // Sequential batch number "status": 200, // HTTP-like status code (200, 207, 400, 500) "data": { "status": "success", // "success", "partial", or "error" "type": "shop-tags", // Matches the top-level type "message": "Records uploaded successfully", // Summary message "request_id": "string", "batch_id": "string", "error": "" // Optional — empty or null on success; contains error details otherwise } } ] } ``` | Field | Type | Description | | --------------------------- | ------ | ------------------------------------------------ | | `request_id` | string | Unique ID for the overall request | | `type` | string | Type of data being processed (e.g., "shop-tags") | | `batches` | array | Array of batch results | | `batches[].batch` | int | Sequential batch number | | `batches[].status` | int | HTTP-like status code (200, 207, 400, 500) | | `batches[].data` | object | Batch result details | | `batches[].data.status` | string | "success", "partial", or "error" | | `batches[].data.type` | string | Matches the top-level type | | `batches[].data.message` | string | Summary message of the batch result | | `batches[].data.request_id` | string | Unique request ID for the batch | | `batches[].data.batch_id` | string | Unique batch ID | | `batches[].data.error` | string | Error details (empty or null on success) | ## Error Codes | Status Code | Description | data.status value | | ----------- | ----------------------------------------- | ----------------- | | 200 | All records processed successfully | success | | 207 | Some records failed, others succeeded | partial | | 400 | Validation or dependency error | error | | 500 | Internal or unexpected server error | error | | 401 | Unauthorized - Invalid or missing API key | error | ## Example ```bash theme={null} curl -X POST https://api.hellocobi.com/v1/data/shop-tags \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "records": [ { "shop_tag_id": "550e8400-e29b-41d4-a716-446655440000", "shop_id": "550e8400-e29b-41d4-a716-446655440001", "tag_id": "550e8400-e29b-41d4-a716-446655440002", "created": "2023-01-15T14:30:00Z", "updated": "2023-06-22T09:15:30Z", "is_deleted": false } ] }' ``` ## Best Practices 1. **Upload Order**: Upload shops and tags before shop-tags to ensure references exist 2. **Batch Size**: Upload up to 5000 records at a time for optimal performance 3. **Data Validation**: Ensure all required fields are present and properly formatted 4. **Error Handling**: Implement proper error handling for failed requests 5. **Rate Limiting**: Be mindful of API rate limits Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Shops Source: https://docs.hellocobi.com/api-reference/data/endpoint/shops POST /v1/data/shops Upload shop records to the Cobi Data API. ## Authentication Required This endpoint requires API key authentication via Bearer token in the Authorization header. ```http theme={null} Authorization: Bearer YOUR_API_KEY Content-Type: application/json ``` ## About the Data API This endpoint is part of the Cobi Data API (`/v1/data/:type`), which provides a consistent interface for uploading different types of structured data. ## Request Body An array of shop records (up to 5000) Unique identifier for the shop Name of the shop Geographic region (2-letter country code, e.g., "CA" for Canada) Flag indicating if the shop is public Timestamp when the record was created Timestamp when the record was last updated Flag indicating if the record is marked as deleted ## Response The API returns a JSON response with the following fields: ```json theme={null} { "request_id": "string", // Unique ID for the overall request "type": "shops", // Type of data being processed "batches": [ { "batch": 1, // Sequential batch number "status": 200, // HTTP-like status code (200, 207, 400, 500) "data": { "status": "success", // "success", "partial", or "error" "type": "shops", // Matches the top-level type "message": "Records uploaded successfully", // Summary message "request_id": "string", "batch_id": "string", "error": "" // Optional — empty or null on success; contains error details otherwise } } ] } ``` | Field | Type | Description | | --------------------------- | ------ | -------------------------------------------- | | `request_id` | string | Unique ID for the overall request | | `type` | string | Type of data being processed (e.g., "shops") | | `batches` | array | Array of batch results | | `batches[].batch` | int | Sequential batch number | | `batches[].status` | int | HTTP-like status code (200, 207, 400, 500) | | `batches[].data` | object | Batch result details | | `batches[].data.status` | string | "success", "partial", or "error" | | `batches[].data.type` | string | Matches the top-level type | | `batches[].data.message` | string | Summary message of the batch result | | `batches[].data.request_id` | string | Unique request ID for the batch | | `batches[].data.batch_id` | string | Unique batch ID | | `batches[].data.error` | string | Error details (empty or null on success) | ## Error Codes | Status Code | Description | data.status value | | ----------- | ----------------------------------------- | ----------------- | | 200 | All records processed successfully | success | | 207 | Some records failed, others succeeded | partial | | 400 | Validation or dependency error | error | | 500 | Internal or unexpected server error | error | | 401 | Unauthorized - Invalid or missing API key | error | ## Example ```bash theme={null} curl -X POST https://api.hellocobi.com/v1/data/shops \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "records": [ { "shop_id": "550e8400-e29b-41d4-a716-446655440000", "name": "Campus Bookstore", "region": "CA", "is_public": true, "created": "2023-01-15T14:30:00Z", "updated": "2023-06-22T09:15:30Z", "is_deleted": false } ] }' ``` ## Best Practices 1. **Batch Size**: Upload up to 5000 records at a time for optimal performance 2. **Data Validation**: Ensure all required fields are present and properly formatted 3. **Error Handling**: Implement proper error handling for failed requests 4. **Rate Limiting**: Be mindful of API rate limits Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Student Favourites Source: https://docs.hellocobi.com/api-reference/data/endpoint/student-favourites POST /v1/data/student-favourites Upload student favourite mappings to the Cobi Data API. **Important**: Student favourite records have dependencies on both customers/students and shops. The system validates both the `student_id` and `shop_id` fields against existing records in the database. You should upload customer/student and shop data before uploading student favourite mappings to ensure proper data validation. ## Authentication Required This endpoint requires API key authentication via Bearer token in the Authorization header. ```http theme={null} Authorization: Bearer YOUR_API_KEY Content-Type: application/json ``` ## About the Data API This endpoint is part of the Cobi Data API (`/v1/data/:type`), which provides a consistent interface for uploading different types of structured data. ## Request Body An array of student favourite mapping records (up to 5000) Unique identifier for the favourite mapping ID of the student (must reference an existing customer/student) ID of the shop (must reference an existing shop) Timestamp when the record was created Timestamp when the record was last updated Flag indicating if the record is marked as deleted ## Response The API returns a JSON response with the following fields: ```json theme={null} { "request_id": "string", // Unique ID for the overall request "type": "student-favourites", // Type of data being processed "batches": [ { "batch": 1, // Sequential batch number "status": 200, // HTTP-like status code (200, 207, 400, 500) "data": { "status": "success", // "success", "partial", or "error" "type": "student-favourites", // Matches the top-level type "message": "Records uploaded successfully", // Summary message "request_id": "string", "batch_id": "string", "error": "" // Optional — empty or null on success; contains error details otherwise } } ] } ``` | Field | Type | Description | | --------------------------- | ------ | --------------------------------------------------------- | | `request_id` | string | Unique ID for the overall request | | `type` | string | Type of data being processed (e.g., "student-favourites") | | `batches` | array | Array of batch results | | `batches[].batch` | int | Sequential batch number | | `batches[].status` | int | HTTP-like status code (200, 207, 400, 500) | | `batches[].data` | object | Batch result details | | `batches[].data.status` | string | "success", "partial", or "error" | | `batches[].data.type` | string | Matches the top-level type | | `batches[].data.message` | string | Summary message of the batch result | | `batches[].data.request_id` | string | Unique request ID for the batch | | `batches[].data.batch_id` | string | Unique batch ID | | `batches[].data.error` | string | Error details (empty or null on success) | ## Error Codes | Status Code | Description | data.status value | | ----------- | ----------------------------------------- | ----------------- | | 200 | All records processed successfully | success | | 207 | Some records failed, others succeeded | partial | | 400 | Validation or dependency error | error | | 500 | Internal or unexpected server error | error | | 401 | Unauthorized - Invalid or missing API key | error | ## Example ```bash theme={null} curl -X POST https://api.hellocobi.com/v1/data/student-favourites \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "records": [ { "favourite_id": "550e8400-e29b-41d4-a716-446655440000", "student_id": "550e8400-e29b-41d4-a716-446655440001", "shop_id": "550e8400-e29b-41d4-a716-446655440002", "created": "2023-01-15T14:30:00Z", "updated": "2023-06-22T09:15:30Z", "is_deleted": false } ] }' ``` ## Best Practices 1. **Upload Order**: Upload customers/students and shops before student-favourites to ensure references exist 2. **Batch Size**: Upload up to 5000 records at a time for optimal performance 3. **Data Validation**: Ensure all required fields are present and properly formatted 4. **Error Handling**: Implement proper error handling for failed requests 5. **Rate Limiting**: Be mindful of API rate limits 6. **Soft Deletion**: Use the is\_deleted flag instead of permanently deleting records Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Tags Source: https://docs.hellocobi.com/api-reference/data/endpoint/tags POST /v1/data/tags Upload tag records to the Cobi Data API. ## Authentication Required This endpoint requires API key authentication via Bearer token in the Authorization header. ```http theme={null} Authorization: Bearer YOUR_API_KEY Content-Type: application/json ``` ## About the Data API This endpoint is part of the Cobi Data API (`/v1/data/:type`), which provides a consistent interface for uploading different types of structured data. ## Request Body An array of tag records (up to 5000) Unique identifier for the tag Name of the tag Timestamp when the record was created Timestamp when the record was last updated Flag indicating if the record is marked as deleted ## Response The API returns a JSON response with the following fields: ```json theme={null} { "request_id": "string", // Unique ID for the overall request "type": "tags", // Type of data being processed "batches": [ { "batch": 1, // Sequential batch number "status": 200, // HTTP-like status code (200, 207, 400, 500) "data": { "status": "success", // "success", "partial", or "error" "type": "tags", // Matches the top-level type "message": "Records uploaded successfully", // Summary message "request_id": "string", "batch_id": "string", "error": "" // Optional — empty or null on success; contains error details otherwise } } ] } ``` | Field | Type | Description | | --------------------------- | ------ | ------------------------------------------- | | `request_id` | string | Unique ID for the overall request | | `type` | string | Type of data being processed (e.g., "tags") | | `batches` | array | Array of batch results | | `batches[].batch` | int | Sequential batch number | | `batches[].status` | int | HTTP-like status code (200, 207, 400, 500) | | `batches[].data` | object | Batch result details | | `batches[].data.status` | string | "success", "partial", or "error" | | `batches[].data.type` | string | Matches the top-level type | | `batches[].data.message` | string | Summary message of the batch result | | `batches[].data.request_id` | string | Unique request ID for the batch | | `batches[].data.batch_id` | string | Unique batch ID | | `batches[].data.error` | string | Error details (empty or null on success) | ## Error Codes | Status Code | Description | data.status value | | ----------- | ----------------------------------------- | ----------------- | | 200 | All records processed successfully | success | | 207 | Some records failed, others succeeded | partial | | 400 | Validation or dependency error | error | | 500 | Internal or unexpected server error | error | | 401 | Unauthorized - Invalid or missing API key | error | ## Example ```bash theme={null} curl -X POST https://api.hellocobi.com/v1/data/tags \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "records": [ { "tag_id": "550e8400-e29b-41d4-a716-446655440000", "name": "Student Discount", "created": "2023-01-15T14:30:00Z", "updated": "2023-06-22T09:15:30Z", "is_deleted": false } ] }' ``` ## Best Practices 1. **Batch Size**: Upload up to 5000 records at a time for optimal performance 2. **Data Validation**: Ensure all required fields are present and properly formatted 3. **Error Handling**: Implement proper error handling for failed requests 4. **Rate Limiting**: Be mindful of API rate limits Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Transactions Source: https://docs.hellocobi.com/api-reference/data/endpoint/transactions POST /v1/data/transactions Upload transaction records to the Cobi Data API. **Important**: Transaction records have a dependency on shops. The system validates the `shop_id` field against existing records in the database. You should upload shops before uploading transactions to ensure proper data validation. ## Authentication Required This endpoint requires API key authentication via Bearer token in the Authorization header. ```http theme={null} Authorization: Bearer YOUR_API_KEY Content-Type: application/json ``` ## About the Data API This endpoint is part of the Cobi Data API (`/v1/data/:type`), which provides a consistent interface for uploading different types of structured data. ## Request Body An array of transaction records (up to 5000) Unique identifier for the transaction ID of the shop where the transaction occurred (must reference an existing shop) Geographic region (2-letter country code, e.g., "CA" for Canada) Amount saved in the transaction (must be a non-negative number) Total transaction amount (must be a positive number) ID of the student making the purchase ID of the offer used ID of the branch location Source of the transaction (e.g., "online", "in-store") Timestamp when the record was created Timestamp when the record was last updated Flag indicating if the record is marked as deleted Name of the discount applied Type of discount (e.g., "percentage", "fixed") Value of the discount ## Response The API returns a JSON response with the following fields: ```json theme={null} { "request_id": "string", // Unique ID for the overall request "type": "transactions", // Type of data being processed "batches": [ { "batch": 1, // Sequential batch number "status": 200, // HTTP-like status code (200, 207, 400, 500) "data": { "status": "success", // "success", "partial", or "error" "type": "transactions", // Matches the top-level type "message": "Records uploaded successfully", // Summary message "request_id": "string", "batch_id": "string", "error": "" // Optional — empty or null on success; contains error details otherwise } } ] } ``` | Field | Type | Description | | --------------------------- | ------ | --------------------------------------------------- | | `request_id` | string | Unique ID for the overall request | | `type` | string | Type of data being processed (e.g., "transactions") | | `batches` | array | Array of batch results | | `batches[].batch` | int | Sequential batch number | | `batches[].status` | int | HTTP-like status code (200, 207, 400, 500) | | `batches[].data` | object | Batch result details | | `batches[].data.status` | string | "success", "partial", or "error" | | `batches[].data.type` | string | Matches the top-level type | | `batches[].data.message` | string | Summary message of the batch result | | `batches[].data.request_id` | string | Unique request ID for the batch | | `batches[].data.batch_id` | string | Unique batch ID | | `batches[].data.error` | string | Error details (empty or null on success) | ## Error Codes | Status Code | Description | data.status value | | ----------- | ----------------------------------------- | ----------------- | | 200 | All records processed successfully | success | | 207 | Some records failed, others succeeded | partial | | 400 | Validation or dependency error | error | | 500 | Internal or unexpected server error | error | | 401 | Unauthorized - Invalid or missing API key | error | ## Example ```bash theme={null} curl -X POST https://api.hellocobi.com/v1/data/transactions \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "records": [ { "transaction_id": "550e8400-e29b-41d4-a716-446655440000", "shop_id": "550e8400-e29b-41d4-a716-446655440002", "region": "CA", "savings": 10.50, "amount": 100.00, "source": "online", "student_id": "550e8400-e29b-41d4-a716-446655440001", "created": "2023-01-15T14:30:00Z", "updated": "2023-06-22T09:15:30Z", "discount_name": "Student Discount", "discount_type": "percentage", "discount_value": 10 } ] }' ``` ## Best Practices 1. **Upload Order**: Upload shops before transactions to ensure references exist 2. **Batch Size**: Upload up to 5000 records at a time for optimal performance 3. **Data Validation**: Ensure all required fields are present and properly formatted 4. **Error Handling**: Implement proper error handling for failed requests 5. **Rate Limiting**: Be mindful of API rate limits Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Interactions Source: https://docs.hellocobi.com/api-reference/data/endpoint/user-interactions POST /user-interactions Record and retrieve user interactions with items (TAG, OFFER, SHOP, KEYWORD). **Important**: User interaction records have dependencies on students. The system validates the `student_id` against existing records in the database. You should upload student data before uploading user interaction records to ensure proper data validation. The `item_id` field must reference the correct item type as specified by `item_type`. ## Authentication Required This endpoint may require API key authentication via Bearer token in the Authorization header. ```http theme={null} Authorization: Bearer YOUR_API_KEY Content-Type: application/json ``` ## About the Endpoint This endpoint allows you to record and retrieve user interactions with items (TAG, OFFER, SHOP, KEYWORD). ### Source Field for Recommendation Feedback The `source` field is crucial for tracking recommendation API effectiveness: * **`null`**: Normal user browsing interactions (not from recommendations) * **`"COBI"`**: Interactions that originated from recommendation API responses This enables the recommendation system to: * Track which recommendations led to user engagement * Measure recommendation algorithm effectiveness * Improve future recommendations based on user behavior patterns ## Request Body Type of interaction. One of: FILTER\_CLICK, SHARE, PAGE\_VIEW, DISCOUNT\_CODE Psuedo ID of the user UUID of the student One of: TAG, OFFER, SHOP UUID of the item (TAG, OFFER, KEYWORD, or SHOP) as determined by `item_type` City where the interaction occurred Country where the interaction occurred One of: ANDROID, IOS, WEB, OTHER Event timestamp (positive integer or ISO datetime string) Source of interaction. Set to 'COBI' when from recommendation API, null otherwise Recommendation ID of cobi recommended interaction, null otherwise ## Response The API returns a JSON response with the created interaction object on success. * `201 Created` on success * `400 Bad Request` if validation fails ## Error Codes | Status Code | Description | | ----------- | -------------------------------- | | 201 | Interaction created successfully | | 400 | Validation error | | 401 | Unauthorized - Invalid API key | | 500 | Internal server error | ## Example ```bash theme={null} curl -X POST https://api.hellocobi.com/user-interactions \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "interaction_type": "FILTER_CLICK", "user_id": "75962b04e5caa7e582e2bc2949587ed9", "student_id": "b1a2c3d4-e5f6-7890-abcd-1234567890ef", "item_type": "TAG", "item_id": "a1b2c3d4-e5f6-7890-abcd-1234567890ab", "city": "Berlin", "country": "Germany", "platform": "WEB", "event_timestamp": 1718035200, "source": "COBI", "recommendation_id": "6fc7eb1d-a516-4a9b-8dcc-313976461ae4" }' ``` # Branch Model Source: https://docs.hellocobi.com/api-reference/data/models/branch The category data model represents information about shop categories in the Cobi system. **Important**: The branch model has a dependency on the shop model. The `shop_id` field must reference an existing shop in the system. It's recommended to upload shop data before uploading branch data. ## Schema ```json theme={null} { "type": "object", "required": ["branch_id", "shop_id", "name", "region"], "properties": { "branch_id": { "type": "string", "format": "uuid", "description": "Unique identifier for the branch" }, "shop_id": { "type": "string", "format": "uuid", "description": "ID of the shop this branch belongs to (must reference an existing shop)" }, "name": { "type": "string", "description": "Name of the branch" }, "region": { "type": "string", "description": "Geographic region (2-letter country code, ISO 3166-1 alpha-2)" }, "latitude": { "type": "number", "nullable": true, "description": "Latitude coordinate of the branch location" }, "longitude": { "type": "number", "nullable": true, "description": "Longitude coordinate of the branch location" }, "created": { "type": "string", "format": "date-time", "nullable": true, "description": "Timestamp when the record was created" }, "updated": { "type": "string", "format": "date-time", "nullable": true, "description": "Timestamp when the record was last updated" }, "is_deleted": { "type": "boolean", "nullable": true, "description": "Flag indicating if the record is marked as deleted" } } } ``` ## Field Descriptions ### Required Fields | Field | Type | Format | Description | | ----------- | ------ | ------ | ------------------------------------------------------------------- | | `branch_id` | string | UUID | Unique identifier for the branch | | `shop_id` | string | UUID | ID of the shop this branch belongs to (must reference a valid shop) | | `name` | string | - | Name of the branch | | `region` | string | - | Geographic region (2-letter country code, e.g., "CA" for Canada) | ### Optional Fields | Field | Type | Format | Description | | ------------ | ------- | --------- | -------------------------------------------------- | | `latitude` | number | - | Latitude coordinate of the branch location | | `longitude` | number | - | Longitude coordinate of the branch location | | `created` | string | date-time | Timestamp when the record was created | | `updated` | string | date-time | Timestamp when the record was last updated | | `is_deleted` | boolean | - | Flag indicating if the record is marked as deleted | ## Example ```json theme={null} { "branch_id": "550e8400-e29b-41d4-a716-446655440000", "shop_id": "550e8400-e29b-41d4-a716-446655440001", "name": "Downtown Branch", "region": "CA", "latitude": 43.65107, "longitude": -79.347015, "created": "2023-01-15T14:30:00Z", "updated": "2023-06-22T09:15:30Z", "is_deleted": false } ``` ## Best Practices 1. **UUID Format**: Use valid UUID v4 format for branch\_id and shop\_id 2. **Data Dependencies**: Ensure shop\_id references an existing shop 3. **Region Codes**: Use ISO 3166-1 alpha-2 country codes (e.g., "CA" for Canada, "US" for United States) 4. **Coordinates**: When providing latitude and longitude, ensure they are valid geographic coordinates 5. **Data Consistency**: Ensure consistent data across all branch records Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Category Model Source: https://docs.hellocobi.com/api-reference/data/models/category The category data model represents information about shop categories in the Cobi system. ## Schema ```json theme={null} { "type": "object", "required": ["category_id", "name", "region"], "properties": { "category_id": { "type": "string", "format": "uuid", "description": "Unique identifier for the category" }, "name": { "type": "string", "description": "Name of the category" }, "region": { "type": "string", "description": "Geographic region (2-letter country code, ISO 3166-1 alpha-2)" }, "created": { "type": "string", "format": "date-time", "nullable": true, "description": "Timestamp when the record was created" }, "updated": { "type": "string", "format": "date-time", "nullable": true, "description": "Timestamp when the record was last updated" }, "is_deleted": { "type": "boolean", "nullable": true, "description": "Flag indicating if the record is marked as deleted" } } } ``` ## Field Descriptions ### Required Fields | Field | Type | Format | Description | | ------------- | ------ | ------ | ---------------------------------------------------------------- | | `category_id` | string | UUID | Unique identifier for the category | | `name` | string | - | Name of the category | | `region` | string | - | Geographic region (2-letter country code, e.g., "CA" for Canada) | ### Optional Fields | Field | Type | Format | Description | | ------------ | ------- | --------- | -------------------------------------------------- | | `created` | string | date-time | Timestamp when the record was created | | `updated` | string | date-time | Timestamp when the record was last updated | | `is_deleted` | boolean | - | Flag indicating if the record is marked as deleted | ## Example ```json theme={null} { "category_id": "550e8400-e29b-41d4-a716-446655440000", "name": "Electronics", "region": "CA", "created": "2023-01-15T14:30:00Z", "updated": "2023-06-22T09:15:30Z", "is_deleted": false } ``` ## Best Practices 1. **UUID Format**: Use valid UUID v4 format for category\_id 2. **Region Codes**: Use ISO 3166-1 alpha-2 country codes (e.g., "CA" for Canada, "US" for United States) 3. **Data Consistency**: Ensure consistent data across all category records Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Course Model Source: https://docs.hellocobi.com/api-reference/data/models/course The course data model represents information about educational courses in the Cobi system. ## Schema ```json theme={null} { "type": "object", "required": ["course_id", "name"], "properties": { "course_id": { "type": "string", "format": "uuid", "description": "Unique identifier for the course" }, "name": { "type": "string", "description": "Name of the course" }, "created": { "type": "string", "format": "date-time", "nullable": true, "description": "Timestamp when the record was created" }, "updated": { "type": "string", "format": "date-time", "nullable": true, "description": "Timestamp when the record was last updated" }, "is_deleted": { "type": "boolean", "nullable": true, "description": "Flag indicating if the record is marked as deleted" } } } ``` ## Field Descriptions ### Required Fields | Field | Type | Format | Description | | ----------- | ------ | ------ | -------------------------------- | | `course_id` | string | UUID | Unique identifier for the course | | `name` | string | - | Name of the course | ### Optional Fields | Field | Type | Format | Description | | ------------ | ------- | --------- | -------------------------------------------------- | | `created` | string | date-time | Timestamp when the record was created | | `updated` | string | date-time | Timestamp when the record was last updated | | `is_deleted` | boolean | - | Flag indicating if the record is marked as deleted | ## Example ```json theme={null} { "course_id": "550e8400-e29b-41d4-a716-446655440000", "name": "Introduction to Computer Science", "created": "2023-01-15T14:30:00Z", "updated": "2023-06-22T09:15:30Z", "is_deleted": false } ``` ## Best Practices 1. **UUID Format**: Use valid UUID v4 format for course\_id 2. **Data Consistency**: Ensure consistent data across all course records 3. **Naming**: Use the official course name as it appears in the institution's catalog 4. **Soft Deletion**: Use the is\_deleted flag instead of permanently removing records Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Customer Model Source: https://docs.hellocobi.com/api-reference/data/models/customer The customer data model represents information about your customers in the Cobi system. ## Schema ```json theme={null} { "type": "object", "required": ["customer_id", "user_id", "region"], "properties": { "customer_id": { "type": "string", "format": "uuid", "description": "Unique identifier for the customer" }, "user_id": { "type": "string", "format": "uuid", "description": "Unique identifier for the user" }, "birthdate": { "type": "string", "format": "date", "nullable": true, "description": "Customer's date of birth" }, "institute_id": { "type": "string", "format": "uuid", "nullable": true, "description": "ID of the educational institute" }, "course_id": { "type": "string", "format": "uuid", "nullable": true, "description": "ID of the course" }, "region": { "type": "string", "description": "Geographic region of the customer (2-letter country code, ISO 3166-1 alpha-2)" }, "gender": { "type": "string", "nullable": true, "description": "Customer's gender" }, "created": { "type": "string", "format": "date-time", "nullable": true, "description": "Timestamp when the record was created" }, "updated": { "type": "string", "format": "date-time", "nullable": true, "description": "Timestamp when the record was last updated" }, "is_deleted": { "type": "boolean", "nullable": true, "description": "Flag indicating if the record is marked as deleted" } } } ``` ## Field Descriptions ### Required Fields | Field | Type | Format | Description | | ------------- | ------ | ------ | ---------------------------------------------------------------- | | `customer_id` | string | UUID | Unique identifier for the customer | | `user_id` | string | UUID | Unique identifier for the user | | `region` | string | - | Geographic region (2-letter country code, e.g., "CA" for Canada) | ### Optional Fields | Field | Type | Format | Description | | -------------- | ------- | --------- | -------------------------------------------------- | | `birthdate` | string | date | Customer's date of birth (YYYY-MM-DD) | | `institute_id` | string | UUID | ID of the educational institute | | `course_id` | string | UUID | ID of the course | | `gender` | string | - | Customer's gender | | `created` | string | date-time | Timestamp when the record was created | | `updated` | string | date-time | Timestamp when the record was last updated | | `is_deleted` | boolean | - | Flag indicating if the record is marked as deleted | ## Example ```json theme={null} { "customer_id": "550e8400-e29b-41d4-a716-446655440000", "user_id": "550e8400-e29b-41d4-a716-446655440001", "birthdate": "1990-01-01", "institute_id": "550e8400-e29b-41d4-a716-446655440002", "course_id": "550e8400-e29b-41d4-a716-446655440003", "region": "CA", "gender": "MALE", "created": "2023-01-15T14:30:00Z", "updated": "2023-06-22T09:15:30Z", "is_deleted": false } ``` ## Best Practices 1. **UUID Format**: Use valid UUID v4 format for all ID fields 2. **Date Format**: Use ISO 8601 date format (YYYY-MM-DD) for birthdate 3. **Region Codes**: Use ISO 3166-1 alpha-2 country codes (e.g., "CA" for Canada, "US" for United States) 4. **Data Consistency**: Ensure consistent data across all customer records Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Institute Model Source: https://docs.hellocobi.com/api-reference/data/models/institute The institute data model represents information about educational institutions in the Cobi system. ## Schema ```json theme={null} { "type": "object", "required": ["institute_id", "name", "region"], "properties": { "institute_id": { "type": "string", "format": "uuid", "description": "Unique identifier for the institute" }, "name": { "type": "string", "description": "Name of the institute" }, "region": { "type": "string", "description": "Geographic region (2-letter country code, ISO 3166-1 alpha-2)" }, "created": { "type": "string", "format": "date-time", "nullable": true, "description": "Timestamp when the record was created" }, "updated": { "type": "string", "format": "date-time", "nullable": true, "description": "Timestamp when the record was last updated" }, "is_deleted": { "type": "boolean", "nullable": true, "description": "Flag indicating if the record is marked as deleted" } } } ``` ## Field Descriptions ### Required Fields | Field | Type | Format | Description | | -------------- | ------ | ------ | ---------------------------------------------------------------- | | `institute_id` | string | UUID | Unique identifier for the institute | | `name` | string | - | Name of the institute | | `region` | string | - | Geographic region (2-letter country code, e.g., "CA" for Canada) | ### Optional Fields | Field | Type | Format | Description | | ------------ | ------- | --------- | -------------------------------------------------- | | `created` | string | date-time | Timestamp when the record was created | | `updated` | string | date-time | Timestamp when the record was last updated | | `is_deleted` | boolean | - | Flag indicating if the record is marked as deleted | ## Example ```json theme={null} { "institute_id": "550e8400-e29b-41d4-a716-446655440000", "name": "University of Toronto", "region": "CA", "created": "2023-01-15T14:30:00Z", "updated": "2023-06-22T09:15:30Z", "is_deleted": false } ``` ## Best Practices 1. **UUID Format**: Use valid UUID v4 format for institute\_id 2. **Region Codes**: Use ISO 3166-1 alpha-2 country codes (e.g., "CA" for Canada, "US" for United States) 3. **Data Consistency**: Ensure consistent data across all institute records 4. **Naming**: Use the official name of the educational institution Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Keyword Model Source: https://docs.hellocobi.com/api-reference/data/models/keyword The keyword data model represents information about keywords in the Cobi system. ## Schema ```json theme={null} { "type": "object", "required": ["keyword_id", "name"], "properties": { "keyword_id": { "type": "string", "format": "uuid", "description": "Unique identifier for the keyword" }, "name": { "type": "string", "description": "Name of the keyword" }, "created": { "type": "string", "format": "date-time", "nullable": true, "description": "Timestamp when the record was created" }, "updated": { "type": "string", "format": "date-time", "nullable": true, "description": "Timestamp when the record was last updated" }, "is_deleted": { "type": "boolean", "nullable": true, "description": "Flag indicating if the record is marked as deleted" } } } ``` ## Field Descriptions ### Required Fields | Field | Type | Format | Description | | ------------ | ------ | ------ | --------------------------------- | | `keyword_id` | string | UUID | Unique identifier for the keyword | | `name` | string | - | Name of the keyword | ### Optional Fields | Field | Type | Format | Description | | ------------ | ------- | --------- | -------------------------------------------------- | | `created` | string | date-time | Timestamp when the record was created | | `updated` | string | date-time | Timestamp when the record was last updated | | `is_deleted` | boolean | - | Flag indicating if the record is marked as deleted | ## Example ```json theme={null} { "keyword_id": "550e8400-e29b-41d4-a716-446655440000", "name": "Student Discount", "created": "2023-01-15T14:30:00Z", "updated": "2023-06-22T09:15:30Z", "is_deleted": false } ``` ## Best Practices 1. **UUID Format**: Use valid UUID v4 format for keyword\_id 2. **Data Consistency**: Ensure consistent data across all keyword records 3. **Naming**: Use clear and descriptive names for keywords Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Offer Model Source: https://docs.hellocobi.com/api-reference/data/models/offer The offer data model represents discounts and promotions in the Cobi system. **Important**: The offer model has a dependency on the shop model. The `shop_id` field must reference an existing shop. It's recommended to upload shop data before uploading offers. ## Schema ```json theme={null} { "type": "object", "required": ["offer_id", "shop_id", "name", "type", "value", "region"], "properties": { "offer_id": { "type": "string", "format": "uuid", "description": "Unique identifier for the offer" }, "shop_id": { "type": "string", "format": "uuid", "description": "ID of the shop offering the discount (must reference an existing shop)" }, "name": { "type": "string", "description": "Name of the offer" }, "type": { "type": "string", "description": "Type of offer (e.g., 'percentage', 'fixed')" }, "value": { "type": "number", "format": "float", "description": "Value of the offer (must be a positive number)" }, "region": { "type": "string", "description": "Geographic region where the offer is valid (2-letter country code, ISO 3166-1 alpha-2)" }, "allowed_from": { "type": "string", "format": "date-time", "nullable": true, "description": "Start date and time of the offer" }, "allowed_to": { "type": "string", "format": "date-time", "nullable": true, "description": "End date and time of the offer" }, "allowed_branches": { "type": "string", "nullable": true, "description": "Comma-separated list of branch IDs where the offer is valid" }, "allowed_days": { "type": "string", "nullable": true, "description": "Comma-separated list of days when the offer is valid" }, "created": { "type": "string", "format": "date-time", "nullable": true, "description": "Timestamp when the record was created" }, "updated": { "type": "string", "format": "date-time", "nullable": true, "description": "Timestamp when the record was last updated" }, "is_deleted": { "type": "boolean", "nullable": true, "description": "Flag indicating if the record is marked as deleted" }, "is_enabled": { "type": "boolean", "nullable": true, "description": "Flag indicating if the offer is currently enabled" } } } ``` ## Field Descriptions ### Required Fields | Field | Type | Format | Description | | ---------- | ------ | ------ | ------------------------------------------------------------------- | | `offer_id` | string | UUID | Unique identifier for the offer | | `shop_id` | string | UUID | ID of the shop offering the discount (must reference existing shop) | | `name` | string | - | Name of the offer | | `type` | string | - | Type of offer (e.g., "percentage", "fixed") | | `value` | number | float | Value of the offer (must be a positive number) | | `region` | string | - | Geographic region (2-letter country code, e.g., "CA" for Canada) | ### Optional Fields | Field | Type | Format | Description | | ------------------ | ------- | --------- | ----------------------------------------------------- | | `allowed_from` | string | date-time | Start date and time of the offer (ISO 8601) | | `allowed_to` | string | date-time | End date and time of the offer (ISO 8601) | | `allowed_branches` | string | - | Comma-separated list of branch IDs | | `allowed_days` | string | - | Comma-separated list of days (e.g., "monday,tuesday") | | `created` | string | date-time | Timestamp when the record was created | | `updated` | string | date-time | Timestamp when the record was last updated | | `is_deleted` | boolean | - | Flag indicating if the record is marked as deleted | | `is_enabled` | boolean | - | Flag indicating if the offer is currently enabled | ## Example ```json theme={null} { "offer_id": "550e8400-e29b-41d4-a716-446655440000", "shop_id": "550e8400-e29b-41d4-a716-446655440001", "name": "Student Discount", "type": "percentage", "value": 10.0, "region": "CA", "allowed_from": "2024-01-01T00:00:00Z", "allowed_to": "2024-12-31T23:59:59Z", "allowed_branches": "branch1,branch2", "allowed_days": "monday,tuesday,wednesday", "created": "2023-01-15T14:30:00Z", "updated": "2023-06-22T09:15:30Z", "is_deleted": false, "is_enabled": true } ``` ## Best Practices 1. **UUID Format**: Use valid UUID v4 format for all ID fields 2. **Date/Time Format**: Use ISO 8601 format for date-time fields 3. **Region Codes**: Use ISO 3166-1 alpha-2 country codes (e.g., "CA" for Canada, "US" for United States) 4. **Offer Types**: Use consistent offer types across your system (e.g., "percentage", "fixed") 5. **Value Format**: Use decimal numbers for monetary values 6. **List Format**: Use comma-separated values for allowed\_branches and allowed\_days 7. **Data Dependencies**: Ensure shop\_id references an existing shop Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Shop Model Source: https://docs.hellocobi.com/api-reference/data/models/shop The shop data model represents information about shops in the Cobi system. ## Schema ```json theme={null} { "type": "object", "required": ["shop_id", "name", "region"], "properties": { "shop_id": { "type": "string", "format": "uuid", "description": "Unique identifier for the shop" }, "name": { "type": "string", "description": "Name of the shop" }, "region": { "type": "string", "description": "Geographic region (2-letter country code, ISO 3166-1 alpha-2)" }, "is_public": { "type": "boolean", "nullable": true, "description": "Flag indicating if the shop is public" }, "created": { "type": "string", "format": "date-time", "nullable": true, "description": "Timestamp when the record was created" }, "updated": { "type": "string", "format": "date-time", "nullable": true, "description": "Timestamp when the record was last updated" }, "is_deleted": { "type": "boolean", "nullable": true, "description": "Flag indicating if the record is marked as deleted" } } } ``` ## Field Descriptions ### Required Fields | Field | Type | Format | Description | | --------- | ------ | ------ | ---------------------------------------------------------------- | | `shop_id` | string | UUID | Unique identifier for the shop | | `name` | string | - | Name of the shop | | `region` | string | - | Geographic region (2-letter country code, e.g., "CA" for Canada) | ### Optional Fields | Field | Type | Format | Description | | ------------ | ------- | --------- | -------------------------------------------------- | | `is_public` | boolean | - | Flag indicating if the shop is public | | `created` | string | date-time | Timestamp when the record was created | | `updated` | string | date-time | Timestamp when the record was last updated | | `is_deleted` | boolean | - | Flag indicating if the record is marked as deleted | ## Example ```json theme={null} { "shop_id": "550e8400-e29b-41d4-a716-446655440000", "name": "Campus Bookstore", "region": "CA", "is_public": true, "created": "2023-01-15T14:30:00Z", "updated": "2023-06-22T09:15:30Z", "is_deleted": false } ``` ## Best Practices 1. **UUID Format**: Use valid UUID v4 format for shop\_id 2. **Region Codes**: Use ISO 3166-1 alpha-2 country codes (e.g., "CA" for Canada, "US" for United States) 3. **Data Consistency**: Ensure consistent data across all shop records Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Shop Category Model Source: https://docs.hellocobi.com/api-reference/data/models/shop-category The shop category data model represents the mappings between shops and categories in the Cobi system. **Important**: The shop category model has dependencies on both the shop and category models. The `shop_id` field must reference an existing shop and the `category_id` field must reference an existing category. It's recommended to upload shop and category data before uploading shop category mappings. ## Schema ```json theme={null} { "type": "object", "required": ["shop_category_id", "shop_id", "category_id", "region"], "properties": { "shop_category_id": { "type": "string", "format": "uuid", "description": "Unique identifier for the shop category mapping" }, "shop_id": { "type": "string", "format": "uuid", "description": "ID of the shop (must reference an existing shop)" }, "category_id": { "type": "string", "format": "uuid", "description": "ID of the category (must reference an existing category)" }, "region": { "type": "string", "description": "Geographic region (2-letter country code, ISO 3166-1 alpha-2)" }, "created": { "type": "string", "format": "date-time", "nullable": true, "description": "Timestamp when the record was created" }, "updated": { "type": "string", "format": "date-time", "nullable": true, "description": "Timestamp when the record was last updated" }, "is_deleted": { "type": "boolean", "nullable": true, "description": "Flag indicating if the record is marked as deleted" } } } ``` ## Field Descriptions ### Required Fields | Field | Type | Format | Description | | ------------------ | ------ | ------ | ---------------------------------------------------------------- | | `shop_category_id` | string | UUID | Unique identifier for the shop category mapping | | `shop_id` | string | UUID | ID of the shop (must reference an existing shop) | | `category_id` | string | UUID | ID of the category (must reference an existing category) | | `region` | string | - | Geographic region (2-letter country code, e.g., "CA" for Canada) | ### Optional Fields | Field | Type | Format | Description | | ------------ | ------- | --------- | -------------------------------------------------- | | `created` | string | date-time | Timestamp when the record was created | | `updated` | string | date-time | Timestamp when the record was last updated | | `is_deleted` | boolean | - | Flag indicating if the record is marked as deleted | ## Example ```json theme={null} { "shop_category_id": "550e8400-e29b-41d4-a716-446655440000", "shop_id": "550e8400-e29b-41d4-a716-446655440001", "category_id": "550e8400-e29b-41d4-a716-446655440002", "region": "CA", "created": "2023-01-15T14:30:00Z", "updated": "2023-06-22T09:15:30Z", "is_deleted": false } ``` ## Best Practices 1. **UUID Format**: Use valid UUID v4 format for all ID fields 2. **Data Dependencies**: Ensure shop\_id references an existing shop and category\_id references an existing category 3. **Region Codes**: Use ISO 3166-1 alpha-2 country codes (e.g., "CA" for Canada, "US" for United States) 4. **Data Consistency**: Ensure consistent data across all shop category mapping records Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Shop Keyword Model Source: https://docs.hellocobi.com/api-reference/data/models/shop-keyword The shop keyword data model represents the mappings between shops and keywords in the Cobi system. **Important**: The shop keyword model has dependencies on both the shop and keyword models. The `shop_id` field must reference an existing shop and the `keyword_id` field must reference an existing keyword. It's recommended to upload shop and keyword data before uploading shop keyword mappings. ## Schema ```json theme={null} { "type": "object", "required": ["shop_keyword_id", "shop_id", "keyword_id"], "properties": { "shop_keyword_id": { "type": "string", "format": "uuid", "description": "Unique identifier for the shop keyword mapping" }, "shop_id": { "type": "string", "format": "uuid", "description": "ID of the shop (must reference an existing shop)" }, "keyword_id": { "type": "string", "format": "uuid", "description": "ID of the keyword (must reference an existing keyword)" }, "created": { "type": "string", "format": "date-time", "nullable": true, "description": "Timestamp when the record was created" }, "updated": { "type": "string", "format": "date-time", "nullable": true, "description": "Timestamp when the record was last updated" }, "is_deleted": { "type": "boolean", "nullable": true, "description": "Flag indicating if the record is marked as deleted" } } } ``` ## Field Descriptions ### Required Fields | Field | Type | Format | Description | | ----------------- | ------ | ------ | ------------------------------------------------------ | | `shop_keyword_id` | string | UUID | Unique identifier for the shop keyword mapping | | `shop_id` | string | UUID | ID of the shop (must reference an existing shop) | | `keyword_id` | string | UUID | ID of the keyword (must reference an existing keyword) | ### Optional Fields | Field | Type | Format | Description | | ------------ | ------- | --------- | -------------------------------------------------- | | `created` | string | date-time | Timestamp when the record was created | | `updated` | string | date-time | Timestamp when the record was last updated | | `is_deleted` | boolean | - | Flag indicating if the record is marked as deleted | ## Example ```json theme={null} { "shop_keyword_id": "550e8400-e29b-41d4-a716-446655440000", "shop_id": "550e8400-e29b-41d4-a716-446655440001", "keyword_id": "550e8400-e29b-41d4-a716-446655440002", "created": "2023-01-15T14:30:00Z", "updated": "2023-06-22T09:15:30Z", "is_deleted": false } ``` ## Best Practices 1. **UUID Format**: Use valid UUID v4 format for all ID fields 2. **Data Dependencies**: Ensure shop\_id references an existing shop and keyword\_id references an existing keyword 3. **Data Consistency**: Ensure consistent data across all shop keyword mapping records Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Shop Tag Model Source: https://docs.hellocobi.com/api-reference/data/models/shop-tag The shop tag data model represents the mappings between shops and tags in the Cobi system. **Important**: The shop tag model has dependencies on both the shop and tag models. The `shop_id` field must reference an existing shop and the `tag_id` field must reference an existing tag. It's recommended to upload shop and tag data before uploading shop tag mappings. ## Schema ```json theme={null} { "type": "object", "required": ["shop_tag_id", "shop_id", "tag_id"], "properties": { "shop_tag_id": { "type": "string", "format": "uuid", "description": "Unique identifier for the shop tag mapping" }, "shop_id": { "type": "string", "format": "uuid", "description": "ID of the shop (must reference an existing shop)" }, "tag_id": { "type": "string", "format": "uuid", "description": "ID of the tag (must reference an existing tag)" }, "created": { "type": "string", "format": "date-time", "nullable": true, "description": "Timestamp when the record was created" }, "updated": { "type": "string", "format": "date-time", "nullable": true, "description": "Timestamp when the record was last updated" }, "is_deleted": { "type": "boolean", "nullable": true, "description": "Flag indicating if the record is marked as deleted" } } } ``` ## Field Descriptions ### Required Fields | Field | Type | Format | Description | | ------------- | ------ | ------ | ------------------------------------------------ | | `shop_tag_id` | string | UUID | Unique identifier for the shop tag mapping | | `shop_id` | string | UUID | ID of the shop (must reference an existing shop) | | `tag_id` | string | UUID | ID of the tag (must reference an existing tag) | ### Optional Fields | Field | Type | Format | Description | | ------------ | ------- | --------- | -------------------------------------------------- | | `created` | string | date-time | Timestamp when the record was created | | `updated` | string | date-time | Timestamp when the record was last updated | | `is_deleted` | boolean | - | Flag indicating if the record is marked as deleted | ## Example ```json theme={null} { "shop_tag_id": "550e8400-e29b-41d4-a716-446655440000", "shop_id": "550e8400-e29b-41d4-a716-446655440001", "tag_id": "550e8400-e29b-41d4-a716-446655440002", "created": "2023-01-15T14:30:00Z", "updated": "2023-06-22T09:15:30Z", "is_deleted": false } ``` ## Best Practices 1. **UUID Format**: Use valid UUID v4 format for all ID fields 2. **Data Dependencies**: Ensure shop\_id references an existing shop and tag\_id references an existing tag 3. **Data Consistency**: Ensure consistent data across all shop tag mapping records Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Student Favourite Model Source: https://docs.hellocobi.com/api-reference/data/models/student-favourite The student favourite data model represents the mappings between students and their favourite shops in the Cobi system. **Important**: The student favourite model has dependencies on both the customer/student and shop models. The `student_id` field must reference an existing customer/student and the `shop_id` field must reference an existing shop. It's recommended to upload customer/student and shop data before uploading student favourite mappings. ## Schema ```json theme={null} { "type": "object", "required": ["student_id", "shop_id"], "properties": { "favourite_id": { "type": "string", "format": "uuid", "description": "Unique identifier for the favourite mapping" }, "student_id": { "type": "string", "format": "uuid", "description": "ID of the student (must reference an existing customer/student)" }, "shop_id": { "type": "string", "format": "uuid", "description": "ID of the shop (must reference an existing shop)" }, "created": { "type": "string", "format": "date-time", "nullable": true, "description": "Timestamp when the record was created" }, "updated": { "type": "string", "format": "date-time", "nullable": true, "description": "Timestamp when the record was last updated" }, "is_deleted": { "type": "boolean", "nullable": true, "description": "Flag indicating if the record is marked as deleted" } } } ``` ## Field Descriptions ### Required Fields | Field | Type | Format | Description | | ------------ | ------ | ------ | --------------------------------------------------------------- | | `student_id` | string | UUID | ID of the student (must reference an existing customer/student) | | `shop_id` | string | UUID | ID of the shop (must reference an existing shop) | ### Optional Fields | Field | Type | Format | Description | | -------------- | ------- | --------- | -------------------------------------------------- | | `favourite_id` | string | UUID | Unique identifier for the favourite mapping | | `created` | string | date-time | Timestamp when the record was created | | `updated` | string | date-time | Timestamp when the record was last updated | | `is_deleted` | boolean | - | Flag indicating if the record is marked as deleted | ## Example ```json theme={null} { "favourite_id": "550e8400-e29b-41d4-a716-446655440000", "student_id": "550e8400-e29b-41d4-a716-446655440001", "shop_id": "550e8400-e29b-41d4-a716-446655440002", "created": "2023-01-15T14:30:00Z", "updated": "2023-06-22T09:15:30Z", "is_deleted": false } ``` ## Best Practices 1. **UUID Format**: Use valid UUID v4 format for all ID fields 2. **Data Dependencies**: Ensure student\_id references an existing customer/student and shop\_id references an existing shop 3. **Data Consistency**: Ensure consistent data across all student favourite mapping records 4. **Soft Deletion**: Use the is\_deleted flag instead of permanently deleting records Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Tag Model Source: https://docs.hellocobi.com/api-reference/data/models/tag The tag data model represents information about tags in the Cobi system. ## Schema ```json theme={null} { "type": "object", "required": ["tag_id", "name"], "properties": { "tag_id": { "type": "string", "format": "uuid", "description": "Unique identifier for the tag" }, "name": { "type": "string", "description": "Name of the tag" }, "created": { "type": "string", "format": "date-time", "nullable": true, "description": "Timestamp when the record was created" }, "updated": { "type": "string", "format": "date-time", "nullable": true, "description": "Timestamp when the record was last updated" }, "is_deleted": { "type": "boolean", "nullable": true, "description": "Flag indicating if the record is marked as deleted" } } } ``` ## Field Descriptions ### Required Fields | Field | Type | Format | Description | | -------- | ------ | ------ | ----------------------------- | | `tag_id` | string | UUID | Unique identifier for the tag | | `name` | string | - | Name of the tag | ### Optional Fields | Field | Type | Format | Description | | ------------ | ------- | --------- | -------------------------------------------------- | | `created` | string | date-time | Timestamp when the record was created | | `updated` | string | date-time | Timestamp when the record was last updated | | `is_deleted` | boolean | - | Flag indicating if the record is marked as deleted | ## Example ```json theme={null} { "tag_id": "550e8400-e29b-41d4-a716-446655440000", "name": "Student Discount", "created": "2023-01-15T14:30:00Z", "updated": "2023-06-22T09:15:30Z", "is_deleted": false } ``` ## Best Practices 1. **UUID Format**: Use valid UUID v4 format for tag\_id 2. **Data Consistency**: Ensure consistent data across all tag records 3. **Naming**: Use clear and descriptive names for tags Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Transaction Model Source: https://docs.hellocobi.com/api-reference/data/models/transaction The transaction data model represents purchase records in the Cobi system, including savings and discount information. **Important**: The transaction model has a dependency on the shop model. The `shop_id` field must reference an existing shop. It's recommended to upload shop data before uploading transactions. ## Schema ```json theme={null} { "type": "object", "required": ["transaction_id", "shop_id", "region", "savings", "amount"], "properties": { "transaction_id": { "type": "string", "format": "uuid", "description": "Unique identifier for the transaction" }, "student_id": { "type": "string", "format": "uuid", "nullable": true, "description": "ID of the student making the purchase" }, "shop_id": { "type": "string", "format": "uuid", "description": "ID of the shop where the transaction occurred (must reference an existing shop)" }, "offer_id": { "type": "string", "format": "uuid", "nullable": true, "description": "ID of the offer used" }, "branch_id": { "type": "string", "format": "uuid", "nullable": true, "description": "ID of the branch location" }, "region": { "type": "string", "description": "Geographic region of the transaction (2-letter country code, ISO 3166-1 alpha-2)" }, "source": { "type": "string", "nullable": true, "description": "Source of the transaction (e.g., 'online', 'in-store')" }, "savings": { "type": "number", "format": "float", "description": "Amount saved in the transaction (must be a non-negative number)" }, "amount": { "type": "number", "format": "float", "description": "Total transaction amount (must be a positive number)" }, "created": { "type": "string", "format": "date-time", "nullable": true, "description": "Timestamp when the record was created" }, "updated": { "type": "string", "format": "date-time", "nullable": true, "description": "Timestamp when the record was last updated" }, "is_deleted": { "type": "boolean", "nullable": true, "description": "Flag indicating if the record is marked as deleted" }, "discount_name": { "type": "string", "nullable": true, "description": "Name of the discount applied" }, "discount_type": { "type": "string", "nullable": true, "description": "Type of discount (e.g., 'percentage', 'fixed')" }, "discount_value": { "type": "number", "format": "float", "nullable": true, "description": "Value of the discount" } } } ``` ## Field Descriptions ### Required Fields | Field | Type | Format | Description | | ---------------- | ------ | ------ | ---------------------------------------------------------------------------- | | `transaction_id` | string | UUID | Unique identifier for the transaction | | `shop_id` | string | UUID | ID of the shop where the transaction occurred (must reference existing shop) | | `region` | string | - | Geographic region (2-letter country code, e.g., "CA" for Canada) | | `savings` | number | float | Amount saved in the transaction (must be a non-negative number) | | `amount` | number | float | Total transaction amount (must be a positive number) | ### Optional Fields | Field | Type | Format | Description | | ---------------- | ------- | --------- | ------------------------------------------------------ | | `student_id` | string | UUID | ID of the student making the purchase | | `offer_id` | string | UUID | ID of the offer used | | `branch_id` | string | UUID | ID of the branch location | | `source` | string | - | Source of the transaction (e.g., "online", "in-store") | | `created` | string | date-time | Timestamp when the record was created | | `updated` | string | date-time | Timestamp when the record was last updated | | `is_deleted` | boolean | - | Flag indicating if the record is marked as deleted | | `discount_name` | string | - | Name of the discount applied | | `discount_type` | string | - | Type of discount (e.g., "percentage", "fixed") | | `discount_value` | number | float | Value of the discount | ## Example ```json theme={null} { "transaction_id": "550e8400-e29b-41d4-a716-446655440000", "student_id": "550e8400-e29b-41d4-a716-446655440001", "shop_id": "550e8400-e29b-41d4-a716-446655440002", "offer_id": "550e8400-e29b-41d4-a716-446655440003", "branch_id": "550e8400-e29b-41d4-a716-446655440004", "region": "CA", "source": "online", "savings": 10.5, "amount": 100.0, "created": "2023-01-15T14:30:00Z", "updated": "2023-06-22T09:15:30Z", "is_deleted": false, "discount_name": "Student Discount", "discount_type": "percentage", "discount_value": 10 } ``` ## Best Practices 1. **UUID Format**: Use valid UUID v4 format for all ID fields 2. **Numeric Values**: Use decimal numbers for monetary values 3. **Region Codes**: Use ISO 3166-1 alpha-2 country codes (e.g., "CA" for Canada, "US" for United States) 4. **Data Consistency**: Ensure consistent data across all transaction records 5. **Discount Information**: Provide complete discount details when available 6. **Data Dependencies**: Ensure shop\_id references an existing shop Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Interaction Model Source: https://docs.hellocobi.com/api-reference/data/models/user-interaction The user interaction data model represents a user's interaction with an item (TAG, OFFER, KEYWORD, or SHOP) in the Cobi system. **Important**: User interaction records have dependencies students. The system validates the `student_id` against existing records in the database. You should upload student data before uploading user interaction records to ensure proper data validation. The `item_id` field must reference the correct item type as specified by `item_type`. ## Schema ```json theme={null} { "type": "object", "required": [ "interaction_type", "student_id", "item_type", "event_timestamp" ], "properties": { "interaction_type": { "type": "string", "description": "Type of interaction. One of: FILTER_CLICK, SHARE, PAGE_VIEW, DISCOUNT_CODE" }, "user_id": { "type": "string", "format": "uuid", "description": "UUID of the student" }, "student_id": { "type": "string", "format": "uuid", "nullable": true, "description": "UUID of the student" }, "item_type": { "type": "string", "description": "One of: TAG, OFFER, SHOP, KEYWORD" }, "item_id": { "type": "string", "format": "uuid", "description": "UUID of the item (TAG, OFFER, KEYWORD or SHOP) as determined by item_type" }, "city": { "type": "string", "description": "City where the interaction occurred" }, "country": { "type": "string", "description": "Country where the interaction occurred" }, "platform": { "type": "string", "description": "One of: ANDROID, IOS, WEB, OTHER" }, "event_timestamp": { "type": ["integer", "string"], "description": "Event timestamp (positive integer or ISO datetime string)" } "source": { "type": "string", "nullable": true, "description": "Source of interaction. Set to 'COBI' when the interaction is from a recommendation API response, null otherwise" }, "recommendation_id": { "type": "string", "nullable": true, "description": "Recommendation ID of cobi recommended interaction, null otherwise" }, } } ``` ## Field Descriptions ### Required Fields | Field | Type | Description | | ------------------ | -------------- | ----------------------------------------------------------------------------- | | `interaction_type` | string | Type of interaction. One of: FILTER\_CLICK, SHARE, PAGE\_VIEW, DISCOUNT\_CODE | | `item_type` | string | One of: TAG, OFFER, SHOP | | `item_id` | string (UUID) | UUID of the item (TAG, OFFER, KEYWORD or SHOP) as determined by `item_type` | | `event_timestamp` | int/ISO string | Event timestamp (positive integer or ISO datetime string) | ### Optional Fields | Field | Type | Description | | ------------------- | ------------- | --------------------------------------------------------------------------------- | | `user_id` | string | Psuedo Id of the student | | `student_id` | string (UUID) | UUID of the student | | `city` | string | City where the interaction occurred | | `country` | string | Country where the interaction occurred | | `platform` | string | One of: ANDROID, IOS, WEB, OTHER | | `source` | string | Source of interaction. Set to 'COBI' when from recommendation API, null otherwise | | `recommendation_id` | string (UUID) | Source of interaction. Set to 'COBI' when from recommendation API, null otherwise | ## Example ```json theme={null} { "interaction_type": "FILTER_CLICK", "user_id": "75962b04e5caa7e582e2bc2949587ed9" "student_id": null, "item_type": "TAG", "item_id": "a1b2c3d4-e5f6-7890-abcd-1234567890ab", "city": "Berlin", "country": "Germany", "platform": "WEB", "event_timestamp": 1718035200, "source": "COBI", "recommendation_id": "6fc7eb1d-a516-4a9b-8dcc-313976461ae4" } ``` ## Source Field for Recommendation Feedback The `source` field is used to track whether user interactions originated from recommendation API responses: * **`null`**: The interaction occurred through normal user browsing (not from a recommendation) * **`"COBI"`**: The interaction occurred when a user clicked on or interacted with an item that was recommended by the Cobi recommendation API This field is crucial for: * **Feedback Loop**: Helps the recommendation system understand which recommendations led to user engagement * **Analytics**: Track the effectiveness of recommendation algorithms * **Model Training**: Improve future recommendations based on user interaction patterns ### Example Usage When a user receives recommendations from the API and clicks on a recommended offer: ```json theme={null} { "interaction_type": "PAGE_VIEW", "student_id": "00000000-9779-438e-ae0d-b52099f6be51", "item_type": "OFFER", "item_id": "00000000-aaca-4844-9cb0-54af8f897322", "event_timestamp": 1718035200, "source": "COBI" } ``` ## Best Practices 1. **Data Validation**: Ensure all required fields are present and properly formatted. 2. **Timestamps**: Use a valid positive integer or ISO 8601 string for `event_timestamp`. 3. **Platform**: Use one of the allowed values for `platform`. 4. **Source Tracking**: Always set `source` to "COBI" when tracking interactions from recommendation API responses. Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Overview Source: https://docs.hellocobi.com/api-reference/data/overview The Cobi Data API provides a consistent interface for uploading different types of structured data into the Cobi system for analytics and segmentation. The Cobi Data API is the foundation for all data integration with the Cobi platform. ## API Structure All Data API endpoints follow the same URL pattern: ```http URL Pattern theme={null} POST /v1/data/:type ``` ```http Customers Example theme={null} POST /v1/data/customers ``` ```http Transactions Example theme={null} POST /v1/data/transactions ``` ```http Offers Example theme={null} POST /v1/data/offers ``` ```http Shops Example theme={null} POST /v1/data/shops ``` ```http Branches Example theme={null} POST /v1/data/branches ``` ```http Categories Example theme={null} POST /v1/data/categories ``` ```http Shop Categories Example theme={null} POST /v1/data/shop-categories ``` ```http Institutes Example theme={null} POST /v1/data/institutes ``` ```http Courses Example theme={null} POST /v1/data/courses ``` ```http Interactions Example theme={null} POST /user-interactions ``` Where `:type` is one of: Upload customer profile information Upload transaction records with savings and discounts Upload offer details for promotions and discounts Upload shop information Upload branch information (requires shops to exist first) Upload category information for product classification Upload shop-category associations (requires shops and categories to exist first) Upload educational institution information Upload course information Upload tag information Upload shop-tag associations (requires shops and tags to exist first) Upload keyword information Upload shop-keyword associations (requires shops and keywords to exist first) Upload student-favourites associations (requires customer and shops to exist first) Record user interactions with items (TAG, OFFER, SHOP) for analytics **Data Dependencies**: Some data types have dependencies on other types. For example, branches require valid shop\_id references. It's important to upload data in the correct order: 1. Upload shops first 2. Then upload branches that reference those shops ## Common Features Each endpoint accepts a batch of records (up to 5000) in a single request: ```json theme={null} { "records": [ { /* record 1 */ }, { /* record 2 */ }, // ...up to 5000 records ] } ``` This batch processing capability allows for efficient data uploads and reduces the number of API calls needed. All endpoints require API key authentication via the Authorization header: ```http theme={null} Authorization: Bearer YOUR_API_KEY Content-Type: application/json ``` Contact our support team to obtain your API key. All endpoints return a consistent response format: ```json theme={null} { "request_id": "string", // Unique ID for the overall request "type": "branches", // Type of data being processed "batches": [ { "batch": 1, // Sequential batch number "status": 200, // HTTP-like status code (200, 207, 400, 500) "data": { "status": "success", // "success", "partial", or "error" "type": "branches", // Matches the top-level type "message": "Records uploaded successfully", // Summary message "request_id": "string", "batch_id": "string", "error": "" // Optional — empty or null on success; contains error details otherwise } } ] } ``` | Field | Type | Description | | --------------------------- | ------ | ----------------------------------------------- | | `request_id` | string | Unique ID for the overall request | | `type` | string | Type of data being processed (e.g., "branches") | | `batches` | array | Array of batch results | | `batches[].batch` | int | Sequential batch number | | `batches[].status` | int | HTTP-like status code (200, 207, 400, 500) | | `batches[].data` | object | Batch result details | | `batches[].data.status` | string | "success", "partial", or "error" | | `batches[].data.type` | string | Matches the top-level type | | `batches[].data.message` | string | Summary message of the batch result | | `batches[].data.request_id` | string | Unique request ID for the batch | | `batches[].data.batch_id` | string | Unique batch ID | | `batches[].data.error` | string | Error details (empty or null on success) | ## Available Endpoints Upload customer profile records with demographic and identification details Upload transaction records with savings, discounts, and purchase information Upload offer details including validity periods and restrictions Upload shop information including location and visibility settings Upload branch information with location coordinates and shop association Upload category information for product classification and organization Upload shop-category associations to organize shops by category Upload educational institution information including region and status Upload course information with official names and status Record user interactions with items (TAG, OFFER, SHOP) for analytics ## Best Practices * Upload up to 5000 records at a time for optimal performance - Consider scheduling regular batch uploads rather than individual API calls - Implement retry logic with exponential backoff for failed requests * Ensure all required fields are present and properly formatted - Validate UUIDs and other formatted fields before sending - Use consistent region codes across all data types * Store your API key securely - Rotate API keys periodically - Use HTTPS for all API requests Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Customer Recommendations Source: https://docs.hellocobi.com/api-reference/recommendations/endpoint/customers GET /v1/recommendations/customers/{customer_id} Get personalized recommendations for a specific customer based on their preferences and behavior. ## Authentication Required This endpoint requires API key authentication via Bearer token in the Authorization header. ```http theme={null} Authorization: Bearer YOUR_API_KEY Content-Type: application/json ``` ## Path Parameters Unique identifier for the customer to get recommendations for ## Query Parameters Whether to include segment-based recommendations in the response. Defaults to false. Filter recommendations by region (e.g., "BH" for Bahrain). Comma-separated values for multiple regions. ## Response The API returns a JSON array of recommendation objects: ```json theme={null} [ { "recommendation_id": "00000000-1234-5678-9abc-def012345678", "name": "Recommended For you", "student_id": "00000000-9779-438e-ae0d-b52099f6be51", "offers": [ "00000000-e9c5-4b43-9d46-4cd02baabc6a", "00000002-c1f0-44d4-af13-32028f522f31", "00000001-4d21-4206-8ced-ff2a11162bf0" ], "type": "SELF" } ] ``` | Field | Type | Description | | ------------------- | ------ | ------------------------------------------------- | | `recommendation_id` | string | Unique identifier for the recommendation | | `name` | string | Human-readable name for the recommendation set | | `student_id` | string | UUID of the customer (matches the path parameter) | | `offers` | array | Array of offer UUIDs that are recommended | | `type` | string | Type of recommendation (SELF or SEGMENT) | ## Error Codes | Status Code | Description | | ----------- | ----------------------------------------- | | 200 | Recommendations retrieved successfully | | 400 | Invalid customer\_id format | | 401 | Unauthorized - Invalid or missing API key | | 404 | Customer not found | | 500 | Internal server error | ## Example ```bash theme={null} curl -X GET "https://api.hellocobi.com/v1/recommendations/customers/00000000-9779-438e-ae0d-b52099f6be51?includeSegments=false®ions=BH" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" ``` ## Best Practices 1. **Caching**: Cache recommendations for frequently requested customers to improve performance 2. **Error Handling**: Implement proper error handling for cases where no recommendations are available 3. **Rate Limiting**: Be mindful of API rate limits (1000 requests per minute) 4. **Validation**: Validate the customer\_id format before making the request Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Segment Recommendations Source: https://docs.hellocobi.com/api-reference/recommendations/endpoint/segments GET /v1/recommendations/segments/{segment_id} Get recommendations for a specific customer segment based on collective preferences. ## Authentication Required This endpoint requires API key authentication via Bearer token in the Authorization header. ```http theme={null} Authorization: Bearer YOUR_API_KEY Content-Type: application/json ``` ## Path Parameters Unique identifier for the customer segment to get recommendations for ## Response The API returns a JSON object with segment recommendations: ```json theme={null} { "recommendation_id": "00000000-1234-5678-9abc-def012345678", "name": "Early Twenties", "segment_id": "cmcapwfrl01zk3m2t0axbh05e", "offers": [ "00000000-aaca-4844-9cb0-54af8f897322", "00000001-c020-4d37-8d6e-4e4e30ff5671", "00000002-5fe2-466c-b58a-5ac8db631334" ], "type": "SEGMENT" } ``` | Field | Type | Description | | ------------------- | ------ | ------------------------------------------------------ | | `recommendation_id` | string | Unique identifier for the recommendation | | `name` | string | Human-readable name for the segment | | `segment_id` | string | Identifier of the segment (matches the path parameter) | | `offers` | array | Array of offer UUIDs that are recommended | | `type` | string | Type of recommendation (SELF or SEGMENT) | ## Error Codes | Status Code | Description | | ----------- | ----------------------------------------- | | 200 | Recommendations retrieved successfully | | 400 | Invalid segment\_id format | | 401 | Unauthorized - Invalid or missing API key | | 404 | Segment not found | | 500 | Internal server error | ## Example ```bash theme={null} curl -X GET https://api.hellocobi.com/v1/recommendations/segments/cmcapwfrl00zk3m2t0axba05e \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" ``` ## Best Practices 1. **Caching**: Cache segment recommendations as they change less frequently than individual customer recommendations 2. **Error Handling**: Implement proper error handling for cases where no recommendations are available 3. **Rate Limiting**: Be mindful of API rate limits (1000 requests per minute) 4. **Validation**: Validate the segment\_id format before making the request Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Customer Recommendation Model Source: https://docs.hellocobi.com/api-reference/recommendations/models/customer-recommendation The customer recommendation model represents personalized recommendations for a specific customer in the Cobi system. ## Schema ```json theme={null} { "type": "object", "required": ["recommendation_id", "name", "student_id", "offers", "type"], "properties": { "recommendation_id": { "type": "string", "format": "uuid", "description": "Unique identifier for the recommendation" }, "name": { "type": "string", "description": "Human-readable name for the recommendation set" }, "student_id": { "type": "string", "format": "uuid", "description": "Unique identifier for the customer" }, "offers": { "type": "array", "items": { "type": "string", "format": "uuid" }, "description": "Array of offer UUIDs that are recommended for the customer" }, "type": { "type": "string", "enum": ["SELF", "SEGMENT"], "description": "Type of recommendation - SELF for individual customer recommendations, SEGMENT for segment-based recommendations" } } } ``` ## Field Descriptions ### Required Fields | Field | Type | Format | Description | | ------------------- | ------ | ------ | ---------------------------------------------- | | `recommendation_id` | string | UUID | Unique identifier for the recommendation | | `name` | string | - | Human-readable name for the recommendation set | | `student_id` | string | UUID | Unique identifier for the customer | | `offers` | array | - | Array of offer UUIDs that are recommended | | `type` | string | enum | Type of recommendation (SELF or SEGMENT) | ## Example ```json theme={null} { "recommendation_id": "00000000-1234-5678-9abc-def012345678", "name": "Recommended For you", "student_id": "00000000-9779-438e-ae0d-b52099f6be51", "offers": [ "00000000-aaca-4844-9cb0-54af8f897322", "00000001-c020-4d37-8d6e-4e4e30ff5671", "00000002-5fe2-466c-b58a-5ac8db631334" ], "type": "SELF" } ``` ## Best Practices 1. **UUID Format**: Ensure all offer IDs are valid UUID v4 format 2. **Data Validation**: Validate that all offer IDs exist in your system before using them 3. **Caching**: Consider caching recommendations to improve performance 4. **Error Handling**: Handle cases where the offers array might be empty Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Segment Recommendation Model Source: https://docs.hellocobi.com/api-reference/recommendations/models/segment-recommendation The segment recommendation model represents recommendations for a specific customer segment in the Cobi system. ## Schema ```json theme={null} { "type": "object", "required": ["recommendation_id", "name", "segment_id", "offers", "type"], "properties": { "recommendation_id": { "type": "string", "format": "uuid", "description": "Unique identifier for the recommendation" }, "name": { "type": "string", "description": "Human-readable name for the segment" }, "segment_id": { "type": "string", "description": "Unique identifier for the customer segment" }, "offers": { "type": "array", "items": { "type": "string", "format": "uuid" }, "description": "Array of offer UUIDs that are recommended for the segment" }, "type": { "type": "string", "enum": ["SELF", "SEGMENT"], "description": "Type of recommendation - SELF for individual customer recommendations, SEGMENT for segment-based recommendations" } } } ``` ## Field Descriptions ### Required Fields | Field | Type | Format | Description | | ------------------- | ------ | ------ | ------------------------------------------ | | `recommendation_id` | string | UUID | Unique identifier for the recommendation | | `name` | string | - | Human-readable name for the segment | | `segment_id` | string | - | Unique identifier for the customer segment | | `offers` | array | - | Array of offer UUIDs that are recommended | | `type` | string | enum | Type of recommendation (SELF or SEGMENT) | ## Example ```json theme={null} { "recommendation_id": "00000000-1234-5678-9abc-def012345678", "name": "Early Twenties", "segment_id": "cmcapwfrl01zk3m2t0axbh05e", "offers": [ "00000000-aaca-4844-9cb0-54af8f897322", "00000001-c020-4d37-8d6e-4e4e30ff5671", "00000002-5fe2-466c-b58a-5ac8db631334" ], "type": "SEGMENT" } ``` ## Best Practices 1. **UUID Format**: Ensure all offer IDs are valid UUID v4 format 2. **Data Validation**: Validate that all offer IDs exist in your system before using them 3. **Caching**: Consider caching segment recommendations as they change less frequently than individual customer recommendations 4. **Error Handling**: Handle cases where the offers array might be empty Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Overview Source: https://docs.hellocobi.com/api-reference/recommendations/overview The Cobi Recommendations API provides personalized recommendations for customers and segments based on their preferences and behavior. The Cobi Recommendations API delivers personalized recommendations to enhance user engagement and improve conversion rates. ## API Structure All Recommendations API endpoints follow the same URL pattern: ```http URL Pattern theme={null} GET /v1/recommendations/:type/:id ``` ```http Customers Example theme={null} GET /v1/recommendations/customers/{customer_id} ``` ```http Segments Example theme={null} GET /v1/recommendations/segments/{segment_id} ``` Where `:type` is one of: Get personalized recommendations for a specific customer Get recommendations for a specific customer segment ## Common Features All endpoints require API key authentication via the Authorization header: ```http theme={null} Authorization: Bearer YOUR_API_KEY Content-Type: application/json ``` Contact our support team to obtain your API key. All endpoints return a consistent response format: ```json theme={null} [ { "name": "string", "student_id": "string (UUID)", "offers": ["string (UUID)"] } ] ``` The response is an array of recommendation objects, each containing: * `name`: Human-readable name for the recommendation set * `student_id` or `segment_id`: Identifier for the customer or segment * `offers`: Array of offer UUIDs that are recommended Recommendations are computationally intensive. Please be mindful of rate limits: * Maximum 1000 requests per minute per API key * Consider caching recommendations for frequently requested customers/segments ## Available Endpoints Get personalized recommendations for a specific customer based on their preferences and behavior Get recommendations for a specific customer segment based on collective preferences ## Best Practices * Cache recommendations for frequently requested customers/segments - Implement proper error handling for failed requests - Use appropriate timeouts for recommendation requests * Ensure customer\_id and segment\_id are valid UUIDs - Handle cases where no recommendations are available - Validate offer IDs returned in recommendations * Store your API key securely - Rotate API keys periodically - Use HTTPS for all API requests Need help? Contact our support team at [support@hellocobi.com](mailto:support@hellocobi.com) # Authentication Source: https://docs.hellocobi.com/authentication Secure your Cobi API requests with Bearer token authentication ## API Keys All Cobi API requests are authenticated with an API key passed as a Bearer token. To get an API key: 1. Contact [support@hellocobi.com](mailto:support@hellocobi.com) with your organisation details and intended use case 2. You'll receive a unique key scoped to your organisation ## Making Authenticated Requests Include your API key in the `Authorization` header on every request: ```bash theme={null} curl -X POST https://api.hellocobi.com/v1/data/customers \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"records": [...]}' ``` ## Security Best Practices Store your API key in environment variables and never hard-code it in source files or commit it to version control. If you suspect a key has been compromised, contact [support@hellocobi.com](mailto:support@hellocobi.com) to get it rotated immediately. All API traffic must use HTTPS. ## Error Responses | Status | Meaning | Resolution | | ------------------ | ---------------------------------------------- | ------------------------------------------------------ | | `401 Unauthorized` | Key is missing or invalid | Check your `Authorization` header format and key value | | `403 Forbidden` | Key does not have permission for this resource | Contact support to verify your key's scope | ```json theme={null} { "error": "Unauthorized", "message": "Invalid or missing API key" } ``` # Backend Service Source: https://docs.hellocobi.com/deployment/backend Environment variables, secrets, and Helm values for the Cobi backend service ## Overview The backend is a Node.js REST API that handles authentication, business logic, data-connector queries, and LLM orchestration. It runs on port `3000` and is exposed through Gateway API or Ingress, usually on its own API hostname. Set `CONNECT_SERVICE_URL` to the in-cluster Connect service URL when `connect.enabled: true`, for example `http://cobi-dashboard-connect:8000`. ## Required Secrets Create the backend Kubernetes Secret **before** running `helm install`. The backend will crash-loop if the Secret is missing or incomplete. ```bash theme={null} kubectl create secret generic cobi-backend-secrets \ --namespace cobi \ # ── Database ───────────────────────────────────────────────────────── --from-literal=DATABASE_URL="postgres://postgres:@:5432/dashboard_auth?schema=public" \ --from-literal=CORE_DATABASE_URL="postgres://postgres:@:5432/cobi_core?schema=public" \ --from-literal=ENABLE_MIGRATIONS="true" \ # ── Auth ───────────────────────────────────────────────────────────── --from-literal=BETTER_AUTH_SECRET="" \ --from-literal=BETTER_AUTH_URL="https://api.example.com" \ --from-literal=FRONTEND_URL="https://app.example.com" \ --from-literal=CONNECT_SERVICE_URL="http://cobi-dashboard-connect:8000" \ # ── LLM inference ──────────────────────────────────────────────────── --from-literal=AI_PROVIDER="openai" \ --from-literal=OPENAI_API_KEY="not-used" \ --from-literal=OPENAI_BASE_URL="http://cobi-dashboard-vllmstack-engine:8000/v1" \ --from-literal=DEFAULT_MODEL_ID="QuantTrio/Qwen3.5-9B-AWQ" \ --from-literal=DEFAULT_FAST_MODEL_ID="QuantTrio/Qwen3.5-9B-AWQ" \ --from-literal=DEFAULT_OCR_MODEL_ID="QuantTrio/Qwen3.5-9B-AWQ" \ # ── Vector store (Qdrant) ──────────────────────────────────────────── --from-literal=QDRANT_URL="http://cobi-dashboard-qdrant:6333" \ --from-literal=QDRANT_LOCAL_ONLY="true" \ --from-literal=QDRANT_API_KEY="" \ --from-literal=QDRANT_CLUSTER_KEY="" \ --from-literal=QDRANT_COLLECTION_PREFIX="prod_" \ # ── Object storage (MinIO or AWS S3) ───────────────────────────────── --from-literal=S3_BUCKET="documents" \ --from-literal=S3_REGION="us-east-1" \ --from-literal=S3_ENDPOINT="http://cobi-dashboard-minio:9000" \ --from-literal=S3_ACCESS_KEY_ID="admin" \ --from-literal=S3_SECRET_ACCESS_KEY="" \ # ── Security ───────────────────────────────────────────────────────── --from-literal=DATASOURCES_ENCRYPTION_KEY="" ``` After the first successful deployment, change `ENABLE_MIGRATIONS` to `"false"` and re-apply the Secret. Running migrations on every pod restart is not safe in production. ## Environment Variable Reference ### Database | Variable | Required | Description | | ------------------- | -------- | ------------------------------------------------------------------------------- | | `DATABASE_URL` | Yes | PostgreSQL connection string for the `dashboard_auth` database (auth, sessions) | | `CORE_DATABASE_URL` | Yes | PostgreSQL connection string for the `cobi_core` database (application data) | | `ENABLE_MIGRATIONS` | Yes | Set `"true"` on first install to run schema migrations; `"false"` after | Both connection strings follow the format: ``` postgres://:@:/?schema=public ``` For the in-cluster PostgreSQL subchart, the host is `-postgresql` (e.g. `cobi-dashboard-postgresql`). ### Authentication | Variable | Required | Description | | -------------------- | -------- | ----------------------------------------------------------------------------- | | `BETTER_AUTH_SECRET` | Yes | Secret key for signing session tokens. Generate with `openssl rand -hex 32` | | `BETTER_AUTH_URL` | Yes | Public HTTPS URL of the application — used for auth callbacks and CSRF checks | | `FRONTEND_URL` | Yes | Public HTTPS URL of the frontend — used for redirect allowlists | `BETTER_AUTH_URL` must match the public API URL. `FRONTEND_URL` must match the public frontend URL. ### LLM Inference | Variable | Required | Description | | ----------------------- | -------- | ---------------------------------------------------------------------------------------------- | | `AI_PROVIDER` | Yes | Always `"openai"` — the backend uses the OpenAI-compatible API | | `OPENAI_BASE_URL` | Yes | Base URL of the vLLM inference service (e.g. `http://cobi-dashboard-vllmstack-engine:8000/v1`) | | `OPENAI_API_KEY` | Yes | API key. Set to any non-empty string for self-hosted vLLM (it ignores the key by default) | | `DEFAULT_MODEL_ID` | Yes | Model ID passed in inference requests (must match the model loaded in vLLM) | | `DEFAULT_FAST_MODEL_ID` | No | Model for fast/cheap requests. Defaults to `DEFAULT_MODEL_ID` if not set | | `DEFAULT_OCR_MODEL_ID` | No | Model for OCR document extraction. Defaults to `DEFAULT_MODEL_ID` if not set | When using OpenRouter or another external provider instead of vLLM: ```bash theme={null} --from-literal=OPENAI_BASE_URL="https://openrouter.ai/api/v1" \ --from-literal=OPENAI_API_KEY="sk-or-..." \ --from-literal=DEFAULT_MODEL_ID="qwen/qwen3.5-9b" ``` ### Vector Store (Qdrant) | Variable | Required | Description | | -------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------- | | `QDRANT_URL` | Yes | HTTP URL of the Qdrant service (e.g. `http://cobi-dashboard-qdrant:6333`) | | `QDRANT_LOCAL_ONLY` | Yes | Set `"true"` for in-cluster or on-prem Qdrant; `"false"` for Qdrant Cloud | | `QDRANT_COLLECTION_PREFIX` | No | String prefix for all collection names. Useful to namespace multiple environments sharing one Qdrant instance (e.g. `"prod_"`, `"staging_"`) | | `QDRANT_API_KEY` | Conditional | Required when using Qdrant Cloud. Leave empty for local | | `QDRANT_CLUSTER_KEY` | Conditional | Required when using Qdrant Cloud. Leave empty for local | ### Object Storage | Variable | Required | Description | | ---------------------- | ----------------------------- | ------------------------------------------------------------------------------------------- | | `S3_BUCKET` | Yes | Bucket name for document storage | | `S3_REGION` | Yes | Signing region. Set to `"us-east-1"` for MinIO | | `S3_ENDPOINT` | Yes for S3-compatible storage | Custom S3 endpoint. Set to `"http://cobi-dashboard-minio:9000"` when using in-cluster MinIO | | `S3_ACCESS_KEY_ID` | Yes for static credentials | S3 access key ID, or MinIO `rootUser` | | `S3_SECRET_ACCESS_KEY` | Yes for static credentials | S3 secret access key, or MinIO `rootPassword` | When `backend.s3.existingSecret` is set, the Helm chart maps these generic `S3_*` Secret keys to the backend container's runtime environment variables. ### Security | Variable | Required | Description | | ---------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------- | | `DATASOURCES_ENCRYPTION_KEY` | Yes | 64-character hex key used to encrypt stored data-source credentials at rest. Generate with `openssl rand -hex 32` | ### Observability | Variable | Required | Description | | ------------------------------ | -------- | ------------------------------------------------------------------------------------------------- | | `OTEL_EXPORTER_OTLP_ENDPOINT` | No | OTLP receiver URL (e.g. `http://cobi-dashboard-otel-lgtm:4318`). Set when otel-lgtm is enabled | | `OTEL_SERVICE_NAME` | No | Service name shown in traces (e.g. `"cobi-backend"`) | | `OTEL_SERVICE_VERSION` | No | Service version shown in traces | | `OTEL_METRIC_EXPORT_INTERVAL` | No | Metric export interval in milliseconds (default `5000`) | | `POSTHOG_API_KEY` | No | PostHog project API key for product analytics | | `POSTHOG_HOST` | No | PostHog ingest host (e.g. `https://eu.i.posthog.com`) | | `POSTHOG_PERSONAL_API_KEY` | No | PostHog personal API key for server-side calls | | `LANGFUSE_PUBLIC_KEY` | No | Langfuse project public key for LLM tracing | | `LANGFUSE_SECRET_KEY` | No | Langfuse project secret key | | `LANGFUSE_HOST` | No | Langfuse host (e.g. `https://cloud.langfuse.com`) | | `LANGFUSE_TRACING_ENVIRONMENT` | No | Environment label in Langfuse traces (e.g. `"production"`) | | `LANGFUSE_PROMPT_RUNTIME_MODE` | No | `"local-only"` to load prompts from bundled files; `"langfuse"` to fetch from Langfuse at runtime | | `LANGFUSE_PROMPT_LABEL` | No | Prompt version label to load from Langfuse (default `"latest"`) | | `LANGFUSE_PROMPT_BUNDLE_PATH` | No | File path of the bundled prompt file when `LANGFUSE_PROMPT_RUNTIME_MODE=local-only` | ## Helm Values Reference the Secret in your values file: ```yaml theme={null} backend: enabled: true image: repository: docker.io/hellocobi/dashboard-backend tag: "0.2.6" pullPolicy: IfNotPresent replicaCount: 1 service: type: ClusterIP port: 3000 envFrom: - secretRef: name: cobi-backend-secrets s3: existingSecret: cobi-backend-secrets resources: requests: cpu: 250m memory: 512Mi limits: cpu: "1" memory: 1Gi ``` ## Generating Secure Keys ```bash theme={null} # BETTER_AUTH_SECRET — 32-byte hex string openssl rand -hex 32 # DATASOURCES_ENCRYPTION_KEY — 32-byte hex string (64 chars) openssl rand -hex 32 ``` # Connect Service Source: https://docs.hellocobi.com/deployment/dashboard-connect Environment variables and secrets required to run the connect service for data source ingestion ## Overview The **connect service** (`hellocobi/dashboard-connect`) is a Python FastAPI service responsible for ingesting data from external data sources. It: * Validates connector credentials against third-party APIs (GitHub, Slack, Linear, HubSpot) * Executes ingestion runs that sync data from those sources into MinIO object storage * Triggers schema mapping syncs requested by the backend It runs on port `8000` as a `ClusterIP` service. By default it is reachable only within the cluster, but the chart can expose it through `gateway.connect` or `ingress.connect` when a deployment needs a public Connect endpoint. Set the backend's `CONNECT_SERVICE_URL` to the in-cluster service URL, usually `http://cobi-dashboard-connect:8000`. ## How Authentication Works Every request from the backend carries the header: ``` x-connect-service-token: ``` The service reads `CONNECT_SERVICE_TOKEN` at startup and validates each incoming request. If the variable is not set, the service **refuses to start**: ``` RuntimeError: CONNECT_SERVICE_TOKEN must be set ``` The same token value must be present on **both** sides — the connect Secret and the backend Secret. ## Required Secrets ### Connect Secret ```bash theme={null} kubectl create secret generic cobi-connect-secrets \ --namespace cobi \ --from-literal=HOST="0.0.0.0" \ --from-literal=PORT="8000" \ --from-literal=CONNECT_SERVICE_TOKEN="" ``` `HOST` must be `0.0.0.0`. The default `127.0.0.1` binds to loopback only — the Kubernetes Service cannot reach the pod. Generate the shared token (use the same value in the backend Secret): ```bash theme={null} openssl rand -hex 32 ``` ### Backend Secret additions The backend also needs `CONNECT_SERVICE_TOKEN` and `CONNECT_BUCKET_PREFIX`. See [Backend Service](/deployment/backend) for the full backend secret. ## Environment Variable Reference ### Server | Variable | Required | Default | Description | | ----------------------- | -------- | ----------- | ----------------------------------------------------------------- | | `HOST` | Yes | `127.0.0.1` | Bind address. **Must be `0.0.0.0`** in a container | | `PORT` | No | `8000` | Port uvicorn listens on. Must match `connect.port` in Helm values | | `CONNECT_SERVICE_TOKEN` | Yes | — | Shared secret for authenticating requests from the backend | ### MinIO Connection The connect service writes ingestion artifacts to MinIO. The bucket, prefix, and per-request credentials are passed in the API request body by the backend, but the S3 **endpoint** for MinIO must be set as an environment variable — it is not included in the request body. | Variable | Required | Description | | ------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `S3_ENDPOINT` | Yes (on-prem) | MinIO endpoint URL — `http://-minio:9000` for in-cluster. DuckDB httpfs reads this to write storage artifacts. Also enables path-style URL mode automatically | ## Helm Values ```yaml theme={null} connect: enabled: true image: repository: docker.io/hellocobi/dashboard-connect tag: "823edd6d96f97aae01425d8c3ddfc9ef8839c65b" pullPolicy: IfNotPresent replicaCount: 1 service: type: ClusterIP port: 8000 envFrom: - secretRef: name: cobi-connect-secrets resources: requests: cpu: 100m memory: 256Mi limits: cpu: 500m memory: 512Mi ``` ## Verify the Service ```bash theme={null} # Port-forward kubectl port-forward -n cobi svc/cobi-dashboard-connect 8000:8000 # Health check — no auth required curl http://localhost:8000/health # {"status":"ok"} # Protected endpoint without token returns 401 curl -X POST http://localhost:8000/internal/mirror-connections/ping # {"detail":"Invalid connect service token"} ``` ## Backend Connection Add `CONNECT_SERVICE_URL` to the backend Secret or `backend.env`: ```yaml theme={null} - name: CONNECT_SERVICE_URL value: "http://cobi-dashboard-connect:8000" ``` Verify it is set: ```bash theme={null} kubectl exec -n cobi deploy/cobi-dashboard-backend -- env | grep CONNECT_SERVICE_URL ``` # Frontend Service Source: https://docs.hellocobi.com/deployment/frontend Environment variables and secrets required to run the Cobi frontend service on-premises ## Overview The frontend is a Vite/React application served as a Node.js process on port `8080`. It is exposed through Gateway API, Ingress, or an OpenShift Route — separate from the backend API hostname. The frontend calls the backend API using `VITE_API_BASE_URL`, which must be set to the backend's public URL. ## Required Secrets ```bash theme={null} kubectl create secret generic cobi-frontend-secrets \ --namespace cobi \ --from-literal=NODE_ENV="production" \ --from-literal=VITE_API_BASE_URL="https://api.example.com" \ --from-literal=VITE_API_SANDBOX_URL="https://api.example.com" ``` `VITE_API_BASE_URL` must match the hostname of the backend Gateway route, Ingress, or Route. ## Environment Variable Reference | Variable | Required | Description | | ---------------------- | -------- | ---------------------------------------------------------------------------------------------------------------- | | `VITE_API_BASE_URL` | Yes | Public base URL of the **backend** API. The frontend constructs all API request URLs from this value | | `VITE_API_SANDBOX_URL` | No | Base URL for sandbox API calls. Set to the same value as `VITE_API_BASE_URL` if only one environment is deployed | | `HOST` | No | Server bind address. Defaults to `0.0.0.0` in the container image — only set if you need to override | ## Helm Values ```yaml theme={null} frontend: enabled: true image: repository: docker.io/hellocobi/dashboard-frontend tag: "0.1.9" pullPolicy: IfNotPresent replicaCount: 1 service: type: ClusterIP port: 8080 envFrom: - secretRef: name: cobi-frontend-secrets resources: requests: cpu: 100m memory: 256Mi limits: cpu: 500m memory: 512Mi ``` ## Notes `VITE_*` variables are injected at **container start time** via a server-side env-injection mechanism in the Cobi image, so Secret changes take effect after a pod restart — no image rebuild is required. # Helm Chart Setup Source: https://docs.hellocobi.com/deployment/helm-setup Step-by-step guide to installing Cobi on Kubernetes or OpenShift using the cobi-dashboard Helm chart ## 1. Choose the chart source ```bash theme={null} CHART_REF=oci://registry-1.docker.io/hellocobi/cobi-dashboard CHART_VERSION=0.1.0 ``` The Helm chart is published as a public OCI artifact. If you are installing from a local checkout of the chart instead, skip these variables and use the local path in the `helm install` commands below. ## 2. Create the namespace ```bash theme={null} kubectl create namespace cobi ``` ## 3. Create required Secrets ### Docker Hub pull secret Required if `global.imagePullSecrets` is set (needed for `hellocobi/*` private images). ```bash theme={null} kubectl create secret docker-registry dockerhub-secret \ --docker-server=https://index.docker.io/v1/ \ --docker-username= \ --docker-password= \ --docker-email= \ --namespace cobi ``` ### Backend environment secret The backend reads its configuration from a Kubernetes Secret. Create it with your environment-specific values: ```bash theme={null} kubectl create secret generic cobi-backend-secrets \ --namespace cobi \ --from-literal=DATABASE_URL="postgres://postgres:@cobi-dashboard-postgresql:5432/dashboard_auth?schema=public" \ --from-literal=CORE_DATABASE_URL="postgres://postgres:@cobi-dashboard-postgresql:5432/cobi_core?schema=public" \ --from-literal=BETTER_AUTH_SECRET="" \ --from-literal=BETTER_AUTH_URL="https://api.example.com" \ --from-literal=FRONTEND_URL="https://app.example.com" \ --from-literal=CONNECT_SERVICE_URL="http://cobi-dashboard-connect:8000" \ --from-literal=QDRANT_URL="http://cobi-dashboard-qdrant:6333" \ --from-literal=S3_BUCKET="documents" \ --from-literal=S3_REGION="us-east-1" \ --from-literal=S3_ENDPOINT="http://cobi-dashboard-minio:9000" \ --from-literal=S3_ACCESS_KEY_ID="admin" \ --from-literal=S3_SECRET_ACCESS_KEY="" \ --from-literal=CONNECT_SERVICE_TOKEN="" \ --from-literal=CONNECT_BUCKET_PREFIX="ingestion" ``` See [Backend Service](/deployment/backend) for the full variable reference. ### Frontend environment secret ```bash theme={null} kubectl create secret generic cobi-frontend-secrets \ --namespace cobi \ --from-literal=NODE_ENV="production" \ --from-literal=VITE_API_BASE_URL="https://api.example.com" \ --from-literal=VITE_API_SANDBOX_URL="https://api.example.com" ``` `VITE_API_BASE_URL` must match the backend Gateway route, Ingress, or Route hostname. ### Connect service secret ```bash theme={null} kubectl create secret generic cobi-connect-secrets \ --namespace cobi \ --from-literal=HOST="0.0.0.0" \ --from-literal=PORT="8000" \ --from-literal=CONNECT_SERVICE_TOKEN="" \ --from-literal=S3_ENDPOINT="http://cobi-dashboard-minio:9000" ``` Use the same `CONNECT_SERVICE_TOKEN` value in both the backend and connect secrets. ## 4. Build chart dependencies If installing from a local chart directory: ```bash theme={null} cd charts/cobi-dashboard helm dependency build ``` This downloads the dependency charts (PostgreSQL, Qdrant, vllm-stack, MinIO) into `charts/`. ## 5. Install Choose a values file that matches your environment, then install: ```bash theme={null} helm install cobi-dashboard "$CHART_REF" \ --namespace cobi \ --version "$CHART_VERSION" \ --values my-values.yaml \ --wait ``` Or from a local chart: ```bash theme={null} helm install cobi-dashboard ./charts/cobi-dashboard \ --namespace cobi \ --values my-values.yaml \ --wait ``` ## 6. Verify the rollout ```bash theme={null} kubectl get pods -n cobi kubectl get gateway,httproute -n cobi # Gateway API kubectl get ingress -n cobi # Kubernetes kubectl get routes -n cobi # OpenShift ``` All pods should reach `Running` / `1/1 Ready`. The vLLM pod takes longer (\~5 min) while it downloads model weights. ## Upgrade ```bash theme={null} helm upgrade cobi-dashboard "$CHART_REF" \ --namespace cobi \ --version "$CHART_VERSION" \ --values my-values.yaml \ --wait ``` ## Uninstall ```bash theme={null} helm uninstall cobi-dashboard --namespace cobi ``` PersistentVolumeClaims are **not** deleted automatically. Remove them manually if you want to purge all data: ```bash theme={null} kubectl delete pvc -n cobi --all ``` *** ## Minimal values file The following enables all application components with sensible defaults. Substitute your image tags and domain names: ```yaml theme={null} global: imagePullSecrets: - dockerhub-secret backend: enabled: true image: repository: docker.io/hellocobi/dashboard-backend tag: "0.2.6" pullPolicy: IfNotPresent envFrom: - secretRef: name: cobi-backend-secrets s3: existingSecret: cobi-backend-secrets frontend: enabled: true image: repository: docker.io/hellocobi/dashboard-frontend tag: "0.1.9" pullPolicy: IfNotPresent envFrom: - secretRef: name: cobi-frontend-secrets connect: enabled: true image: repository: docker.io/hellocobi/dashboard-connect tag: "823edd6d96f97aae01425d8c3ddfc9ef8839c65b" pullPolicy: IfNotPresent envFrom: - secretRef: name: cobi-connect-secrets ingress: enabled: true className: nginx tls: - secretName: cobi-dashboard-tls hosts: - app.example.com - api.example.com - connect.example.com app: enabled: true host: app.example.com api: enabled: true host: api.example.com connect: enabled: true host: connect.example.com minio: enabled: false grafana: enabled: false gateway: enabled: false postgresql: enabled: true auth: username: postgres password: "" database: dashboard_auth primary: initdb: scripts: create-core-db.sql: | CREATE DATABASE cobi_core; persistence: enabled: true size: 10Gi qdrant: enabled: true persistence: enabled: true size: 10Gi minio: enabled: true auth: rootUser: admin rootPassword: "" defaultBuckets: "documents" persistence: enabled: true size: 100Gi otelLgtm: enabled: true # vLLM disabled by default — see vLLM Setup guide vllmstack: enabled: false ``` ## OpenShift values override For OpenShift, disable Kubernetes Ingress and Gateway, then enable the chart's frontend and backend API Routes: ```yaml theme={null} ingress: enabled: false gateway: enabled: false # Enable OpenShift Routes openshift: route: enabled: true host: dashboard.apps.cluster.example.com api: enabled: true host: api.apps.cluster.example.com ``` OpenShift Routes use edge TLS termination by default — TLS is terminated at the router, plain HTTP to the pods. Update the frontend and backend secrets to use the frontend Route and API Route: ```bash theme={null} # Frontend secret --from-literal=VITE_API_BASE_URL="https://api.apps.cluster.example.com" --from-literal=VITE_API_SANDBOX_URL="https://api.apps.cluster.example.com" # Backend secret --from-literal=BETTER_AUTH_URL="https://api.apps.cluster.example.com" --from-literal=FRONTEND_URL="https://dashboard.apps.cluster.example.com" ``` See [Ingress & Routes](/deployment/ingress-routes) for the full platform-specific configuration. # Gateway, Ingress & Routes Source: https://docs.hellocobi.com/deployment/ingress-routes Configure Gateway API, Kubernetes Ingress, or OpenShift Routes for Cobi ## Platform Choice The chart supports Gateway API, Kubernetes Ingress, and OpenShift Routes for the user-facing app/API path. Enable **one** depending on your platform. | Platform | Mechanism | Values key | | --------------------- | ------------------------------ | ------------------------- | | Kubernetes, preferred | Gateway API with Envoy Gateway | `gateway.enabled` | | Kubernetes, nginx | `Ingress` | `ingress.enabled` | | OpenShift | `Route` | `openshift.route.enabled` | *** ## Kubernetes — Gateway API with Envoy Gateway Gateway API is the preferred option for the kOps v2 rehearsal profile and for self-hosted clusters that already run Envoy Gateway. ### Prerequisites Install Gateway API CRDs, cert-manager with Gateway support, and Envoy Gateway: ```bash theme={null} kubectl apply --server-side -f \ https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.1/standard-install.yaml helm upgrade --install cert-manager oci://quay.io/jetstack/charts/cert-manager \ --version v1.20.2 \ --namespace cert-manager \ --create-namespace \ --set crds.enabled=true \ --set config.enableGatewayAPI=true helm upgrade --install eg oci://docker.io/envoyproxy/gateway-helm \ --version v1.7.3 \ --namespace envoy-gateway-system \ --create-namespace ``` Create or reuse a `GatewayClass` named `eg`. On AWS kOps, the kOps v2 reference configures Envoy's data-plane Service as an internet-facing NLB. ### Values ```yaml theme={null} ingress: enabled: false gateway: enabled: true gatewayClass: create: false name: eg annotations: cert-manager.io/cluster-issuer: letsencrypt-prod tls: enabled: true app: secretName: cobi-dashboard-app-tls api: secretName: cobi-dashboard-api-tls connect: secretName: cobi-dashboard-connect-tls app: enabled: true hostname: app.example.com api: enabled: true hostname: api.example.com timeouts: request: 0s backendRequest: 0s connect: enabled: true hostname: connect.example.com timeouts: request: 0s backendRequest: 0s minio: enabled: false grafana: enabled: false ``` Set the corresponding application URLs: ```bash theme={null} # Frontend --from-literal=VITE_API_BASE_URL="https://api.example.com" --from-literal=VITE_API_SANDBOX_URL="https://api.example.com" # Backend --from-literal=BETTER_AUTH_URL="https://api.example.com" --from-literal=FRONTEND_URL="https://app.example.com" --from-literal=CONNECT_SERVICE_URL="http://cobi-dashboard-connect:8000" ``` ### Verify ```bash theme={null} kubectl get gateway -n cobi kubectl get httproute -n cobi kubectl get svc -n envoy-gateway-system ``` *** ## Kubernetes — Ingress The frontend and backend each get their own Ingress and their own hostname. The frontend makes API calls to the backend hostname directly — there is no `/api` path proxying. ### Prerequisites Install `ingress-nginx` before deploying the chart: ```bash theme={null} helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx helm repo update helm install ingress-nginx ingress-nginx/ingress-nginx \ --namespace ingress-nginx \ --create-namespace \ --set controller.replicaCount=2 ``` Wait for the LoadBalancer IP or hostname: ```bash theme={null} kubectl get svc -n ingress-nginx ingress-nginx-controller \ -o jsonpath='{.status.loadBalancer.ingress[0].ip}' ``` Create DNS A records for both hostnames pointing at this address. ### Separate-host values Ingress is configured under the top-level `ingress` block: ```yaml theme={null} gateway: enabled: false ingress: enabled: true className: nginx annotations: {} tls: - secretName: cobi-dashboard-tls hosts: - app.example.com - api.example.com - connect.example.com app: enabled: true host: app.example.com api: enabled: true host: api.example.com connect: enabled: true host: connect.example.com minio: enabled: false grafana: enabled: false ``` Set the corresponding URLs in the frontend and backend Secrets: ```bash theme={null} # Frontend — points at the backend hostname --from-literal=VITE_API_BASE_URL="https://api.example.com" # Backend — points at itself, the frontend, and the in-cluster Connect service --from-literal=BETTER_AUTH_URL="https://api.example.com" --from-literal=FRONTEND_URL="https://app.example.com" --from-literal=CONNECT_SERVICE_URL="http://cobi-dashboard-connect:8000" ``` ### Shared-host values Shared-host mode routes the frontend at `/` and the backend at `ingress.apiPrefix`: ```yaml theme={null} ingress: enabled: true className: nginx host: cobi.example.com apiPrefix: /api tls: - secretName: cobi-dashboard-tls hosts: - cobi.example.com ``` ### TLS — cert-manager (recommended) ```bash theme={null} helm repo add jetstack https://charts.jetstack.io helm install cert-manager jetstack/cert-manager \ --namespace cert-manager \ --create-namespace \ --set crds.enabled=true ``` ```yaml theme={null} # cluster-issuer.yaml apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: letsencrypt-prod spec: acme: server: https://acme-v02.api.letsencrypt.org/directory email: admin@example.com privateKeySecretRef: name: letsencrypt-prod solvers: - http01: ingress: ingressClassName: nginx ``` ```bash theme={null} kubectl apply -f cluster-issuer.yaml ``` Add the annotation under the top-level `ingress` block: ```yaml theme={null} ingress: annotations: cert-manager.io/cluster-issuer: "letsencrypt-prod" tls: - secretName: cobi-dashboard-tls hosts: - app.example.com - api.example.com ``` ### TLS — Pre-existing certificates ```bash theme={null} kubectl create secret tls cobi-dashboard-tls --cert=tls.crt --key=tls.key --namespace cobi ``` *** ## OpenShift — Frontend and API Routes OpenShift uses Routes instead of Ingress. The chart creates Routes for the frontend and backend API. Connect stays internal through `CONNECT_SERVICE_URL` unless your deployment needs a public Connect endpoint. ### Values ```yaml theme={null} ingress: enabled: false gateway: enabled: false # Enable OpenShift Routes openshift: route: enabled: true host: dashboard.apps.cluster.example.com api: enabled: true host: api.apps.cluster.example.com ``` The Routes use **edge TLS termination** — TLS is terminated at the OpenShift router, plain HTTP to the pods. ### Generated Route resources ```yaml theme={null} apiVersion: route.openshift.io/v1 kind: Route metadata: name: cobi-dashboard-frontend namespace: cobi spec: host: dashboard.apps.cluster.example.com to: kind: Service name: cobi-dashboard-frontend port: targetPort: http tls: termination: edge --- apiVersion: route.openshift.io/v1 kind: Route metadata: name: cobi-dashboard-backend namespace: cobi spec: host: api.apps.cluster.example.com to: kind: Service name: cobi-dashboard-backend port: targetPort: http tls: termination: edge ``` Set the corresponding URLs in the frontend and backend Secrets: ```bash theme={null} # Frontend — points at the backend route hostname --from-literal=VITE_API_BASE_URL="https://api.apps.cluster.example.com" # Backend — use the public API URL created for your backend --from-literal=BETTER_AUTH_URL="https://api.apps.cluster.example.com" --from-literal=FRONTEND_URL="https://dashboard.apps.cluster.example.com" ``` *** ## Observability (Grafana) Expose the Grafana UI from the `otel-lgtm` component when `otelLgtm.enabled: true`: ```yaml theme={null} ingress: enabled: true grafana: enabled: true host: grafana.example.com ``` *** ## Internal Service Addresses When configuring Secrets, use these ClusterIP hostnames (release name `cobi-dashboard`, namespace `cobi`): | Service | Internal address | | --------------- | -------------------------------------- | | Backend | `cobi-dashboard-backend:3000` | | Frontend | `cobi-dashboard-frontend:8080` | | Connect | `cobi-dashboard-connect:8000` | | PostgreSQL | `cobi-dashboard-postgresql:5432` | | Qdrant | `cobi-dashboard-qdrant:6333` | | MinIO (API) | `cobi-dashboard-minio:9000` | | MinIO (Console) | `cobi-dashboard-minio:9001` | | vLLM | `cobi-dashboard-vllmstack-engine:8000` | # MinIO Setup Source: https://docs.hellocobi.com/deployment/minio-setup Configure MinIO object storage for document uploads and data ingestion artifacts in on-premises environments ## Overview MinIO provides an S3-compatible API that Cobi uses to store uploaded documents and data ingestion artifacts. Enable it for on-premises or air-gapped clusters. **Default ports:** * `9000` — S3 API (used by the backend and connect service) * `9001` — Web console ## Configuration ```yaml theme={null} minio: enabled: true mode: standalone # "standalone" for single-node; "distributed" for HA auth: rootUser: admin rootPassword: "" # minimum 8 characters # In production, prefer an existing Secret — see below defaultBuckets: "documents" # created on first startup persistence: enabled: true storageClass: "" # use cluster default; set "local-path" or "nfs-csi" for on-prem accessModes: - ReadWriteOnce size: 100Gi service: type: ClusterIP ports: api: 9000 console: 9001 resources: requests: cpu: 250m memory: 512Mi limits: cpu: "1" memory: 1Gi ``` ## On-Premises StorageClass ```yaml theme={null} minio: persistence: storageClass: "local-path" size: 500Gi ``` `standalone` mode with `ReadWriteOnce` works on most on-prem clusters. For production HA, use `mode: distributed` with a StorageClass that supports concurrent access (NFS, Longhorn, etc.). ## Using an Existing Secret ```bash theme={null} kubectl create secret generic minio-credentials \ --namespace cobi \ --from-literal=root-user=admin \ --from-literal=root-password="" ``` ```yaml theme={null} minio: auth: existingSecret: "minio-credentials" ``` ## Exposing the Web Console (optional) ```yaml theme={null} minio: consoleIngress: enabled: true ingressClassName: nginx hostname: minio-console.example.com tls: false ``` Enable TLS before exposing MinIO outside the cluster. Plain HTTP transmits credentials in the clear. ## Verify MinIO ```bash theme={null} # Port-forward the MinIO API kubectl port-forward -n cobi svc/cobi-dashboard-minio 9000:9000 # Health check curl http://localhost:9000/minio/health/live # List buckets mc alias set local http://localhost:9000 admin mc ls local/ ``` The `documents` bucket should exist after first startup. ## Connecting the Backend Set these variables in the backend Secret using the S3 environment variable names: ```bash theme={null} --from-literal=S3_BUCKET="documents" \ --from-literal=S3_REGION="us-east-1" \ --from-literal=S3_ENDPOINT="http://cobi-dashboard-minio:9000" \ --from-literal=S3_ACCESS_KEY_ID="admin" \ --from-literal=S3_SECRET_ACCESS_KEY="" ``` | Variable | Description | | ---------------------- | ----------------------------------------------------------------- | | `S3_BUCKET` | Bucket name — must match `minio.defaultBuckets` | | `S3_REGION` | Signing region. Use `us-east-1` for MinIO | | `S3_ENDPOINT` | MinIO API endpoint. For in-cluster: `http://-minio:9000` | | `S3_ACCESS_KEY_ID` | MinIO `rootUser` | | `S3_SECRET_ACCESS_KEY` | MinIO `rootPassword` | Reference the same Secret from `backend.s3.existingSecret` so Helm projects the generic keys into the backend container. ## Connecting the Connect Service The connect service also writes ingestion artifacts to MinIO. Set the endpoint using the env vars the service reads from (these are the actual variable names the Python process reads): ```bash theme={null} --from-literal=S3_ENDPOINT="http://cobi-dashboard-minio:9000" \ --from-literal=AWS_ACCESS_KEY_ID="admin" \ --from-literal=AWS_SECRET_ACCESS_KEY="" \ --from-literal=AWS_DEFAULT_REGION="us-east-1" ``` See [Connect Service](/deployment/dashboard-connect) for the full secret. ## Sizing Guide | Document volume | Recommended PVC size | | ------------------------ | --------------------------- | | \< 10 000 documents | 50 Gi | | 10 000–100 000 documents | 100–500 Gi | | > 100 000 documents | 500 Gi+ or distributed mode | # Model Storage Source: https://docs.hellocobi.com/deployment/model-storage Host model weights in your own Git repository or MinIO instead of pulling from Hugging Face Hub ## Overview By default, vLLM downloads model weights from Hugging Face Hub at pod startup. In air-gapped or regulated environments you may prefer to host weights in your own infrastructure and have the cluster pull from there instead. Two approaches are supported: | Approach | Best for | | ----------- | ------------------------------------------------------------------------------------ | | **Git LFS** | Model versioning alongside code, existing Git infrastructure (GitHub, GitLab, Gitea) | | **MinIO** | Simplest path — you already have MinIO running in the cluster | Both approaches use the same pattern: 1. Upload model weights to your storage once, from a machine with internet access. 2. An init container on the vLLM pod copies weights to the PVC before vLLM starts. 3. vLLM loads from the local PVC path — no HuggingFace token required. *** ## Option A — Git LFS ### 1. Set up Git LFS in your repository Git LFS stores large binary files (model weights) in a separate blob store while keeping pointer files in the repo. It is supported by GitHub, GitLab, Gitea, and Bitbucket. ```bash theme={null} # Install Git LFS (one-time, on your workstation) git lfs install # In your model repository git lfs track "*.safetensors" "*.bin" "*.pt" "*.gguf" git add .gitattributes git commit -m "track model weight files with LFS" ``` ### 2. Download and push model weights On a machine with internet access: ```bash theme={null} # Download from Hugging Face pip install huggingface_hub huggingface-cli download QuantTrio/Qwen3.5-9B-AWQ \ --local-dir ./qwen35-9b-awq # Push to your Git repository cd your-model-repo cp -r ./qwen35-9b-awq ./models/qwen35-9b-awq git add models/qwen35-9b-awq git commit -m "add Qwen3.5-9B-AWQ weights" git push ``` LFS objects (the large weight files) are uploaded to your Git provider's LFS storage automatically. ### 3. Create a pull secret in the cluster Create a read-only access token in your Git provider, then store it as a Kubernetes Secret: ```bash theme={null} kubectl create secret generic model-repo-credentials \ --namespace cobi \ --from-literal=GIT_REPO_URL="https://github.com/your-org/your-model-repo.git" \ --from-literal=GIT_USERNAME="" \ --from-literal=GIT_TOKEN="" ``` For GitHub, create a fine-grained PAT with read-only access to the model repository. For GitLab, use a project deploy token with `read_repository` scope. For Gitea, create an application token. ### 4. Configure the vLLM init container Replace the `busybox` init container with an `alpine/git` image that clones the repo and pulls LFS files. The model weights land on the PVC at `/data/model` before vLLM starts. ```yaml theme={null} vllmstack: enabled: true servingEngineSpec: enableEngine: true runtimeClassName: "nvidia" tolerations: - key: "nvidia.com/gpu" operator: "Exists" effect: "NoSchedule" modelSpec: - name: "qwen35-9b" repository: "vllm/vllm-openai" tag: "latest" modelURL: "/data/model/models/qwen35-9b-awq" # local path — no HF download replicaCount: 1 requestCPU: 4 requestMemory: "28Gi" requestGPU: 1 limitCPU: "4" limitMemory: "28Gi" shmSize: "2Gi" pvcStorage: "80Gi" pvcAccessMode: - ReadWriteOnce env: - name: TMPDIR value: /data/tmp - name: GIT_REPO_URL valueFrom: secretKeyRef: name: model-repo-credentials key: GIT_REPO_URL - name: GIT_USERNAME valueFrom: secretKeyRef: name: model-repo-credentials key: GIT_USERNAME - name: GIT_TOKEN valueFrom: secretKeyRef: name: model-repo-credentials key: GIT_TOKEN initContainer: name: pull-model-weights image: alpine/git:2.43.0 command: ["sh"] args: - -c - | set -e mkdir -p /data/tmp /data/model /data/.cache chmod -R 777 /data # Skip clone if weights already exist (pod restart) if [ -d /data/model/models/qwen35-9b-awq ]; then echo "Model weights already present, skipping clone." exit 0 fi # Install git-lfs apk add --no-cache git-lfs git lfs install # Clone with embedded credentials (shallow, LFS enabled) REPO_URL="https://${GIT_USERNAME}:${GIT_TOKEN}@$(echo $GIT_REPO_URL | sed 's|https://||')" git clone --depth=1 "$REPO_URL" /data/model echo "Model weights pulled successfully." mountPvcStorage: true vllmConfig: dtype: "float16" tensorParallelSize: 1 maxModelLen: 8192 gpuMemoryUtilization: 0.90 enableChunkedPrefill: true enablePrefixCaching: false maxNumSeqs: 16 extraArgs: - "--trust-remote-code" - "--quantization" - "awq" - "--enforce-eager" - "--attention-backend" - "flashinfer" - "--max-num-batched-tokens" - "1024" - "--disable-log-stats" routerSpec: enableRouter: false ``` The init container skips cloning if the weights directory already exists. This avoids re-downloading on pod restarts. If you push new weights to the repo, delete the PVC and let the pod reprovision. *** ## Option B — MinIO Since MinIO is already running in the cluster, you can use it as a model registry. Upload weights once; the init container fetches them on first pod start. ### 1. Upload model weights to MinIO On a machine with internet access, download the weights and push to MinIO: ```bash theme={null} # Download from Hugging Face huggingface-cli download QuantTrio/Qwen3.5-9B-AWQ \ --local-dir ./qwen35-9b-awq # Configure MinIO client (port-forward first if uploading remotely) kubectl port-forward -n cobi svc/cobi-dashboard-minio 9000:9000 mc alias set cobi http://localhost:9000 admin # Create a models bucket and upload mc mb cobi/models mc cp --recursive ./qwen35-9b-awq cobi/models/qwen35-9b-awq/ ``` Verify: ```bash theme={null} mc ls cobi/models/qwen35-9b-awq/ ``` ### 2. Configure the vLLM init container Replace the `busybox` init container with `minio/mc` to pull weights from the `models` bucket to the PVC: ```yaml theme={null} vllmstack: enabled: true servingEngineSpec: enableEngine: true runtimeClassName: "nvidia" tolerations: - key: "nvidia.com/gpu" operator: "Exists" effect: "NoSchedule" modelSpec: - name: "qwen35-9b" repository: "vllm/vllm-openai" tag: "latest" modelURL: "/data/model" # local path on PVC replicaCount: 1 requestCPU: 4 requestMemory: "28Gi" requestGPU: 1 limitCPU: "4" limitMemory: "28Gi" shmSize: "2Gi" pvcStorage: "80Gi" pvcAccessMode: - ReadWriteOnce env: - name: TMPDIR value: /data/tmp - name: MINIO_ENDPOINT value: "http://cobi-dashboard-minio:9000" - name: MINIO_ACCESS_KEY valueFrom: secretKeyRef: name: minio-credentials key: root-user - name: MINIO_SECRET_KEY valueFrom: secretKeyRef: name: minio-credentials key: root-password initContainer: name: pull-model-weights image: minio/mc:latest command: ["sh"] args: - -c - | set -e mkdir -p /data/tmp /data/.cache chmod -R 777 /data # Skip download if weights already present if [ -f /data/model/config.json ]; then echo "Model weights already present, skipping download." exit 0 fi mc alias set storage "$MINIO_ENDPOINT" "$MINIO_ACCESS_KEY" "$MINIO_SECRET_KEY" mc cp --recursive storage/models/qwen35-9b-awq/ /data/model/ echo "Model weights downloaded successfully." mountPvcStorage: true vllmConfig: dtype: "float16" tensorParallelSize: 1 maxModelLen: 8192 gpuMemoryUtilization: 0.90 enableChunkedPrefill: true enablePrefixCaching: false maxNumSeqs: 16 extraArgs: - "--trust-remote-code" - "--quantization" - "awq" - "--enforce-eager" - "--attention-backend" - "flashinfer" - "--max-num-batched-tokens" - "1024" - "--disable-log-stats" routerSpec: enableRouter: false ``` `minio-credentials` is the same Secret used by the MinIO subchart itself. See [MinIO Setup](/deployment/minio-setup#using-an-existing-secret) for how to create it. The secret keys are `root-user` and `root-password`. *** ## Monitoring the Init Container ```bash theme={null} # Watch init container logs while the pod starts kubectl logs -n cobi -l app.kubernetes.io/name=vllmstack \ -c pull-model-weights --follow # Check pod events if the init container fails kubectl describe pod -n cobi -l app.kubernetes.io/name=vllmstack ``` A successful pull ends with: ``` Model weights downloaded successfully. ``` After the init container exits, vLLM starts and loads from `/data/model`. *** ## Updating the Model To deploy a new version of the weights: ```bash theme={null} # Delete the PVC so the init container re-downloads on next pod start kubectl delete pvc -n cobi -l app.kubernetes.io/name=vllmstack # The next helm upgrade or pod restart triggers a fresh pull helm upgrade cobi-dashboard oci://registry-1.docker.io/hellocobi/cobi-dashboard \ --namespace cobi \ --version 0.1.0 \ --values my-values.yaml \ --wait ``` # On-Premises Deployment Overview Source: https://docs.hellocobi.com/deployment/overview Architecture overview for deploying Cobi on Kubernetes or OpenShift using the cobi-dashboard Helm chart ## Architecture The `cobi-dashboard` Helm chart deploys the full Cobi platform as a set of Kubernetes workloads. All components run in a single namespace and communicate over the cluster network. ```mermaid theme={null} flowchart TB Browser[User browser] subgraph Cluster[Kubernetes / OpenShift namespace] Edge{Gateway / Ingress / Route} Frontend[frontend
Vite / React
:8080] Backend[backend
Node.js API
:3000] Connect[connect
ingestion service
:8000] PostgreSQL[(PostgreSQL
:5432)] Qdrant[(Qdrant
:6333 / :6334)] MinIO[(MinIO
S3 :9000)] VLLM[vLLM stack
OpenAI-compatible
:8000] Observability[otel-lgtm
OTLP :4318
Grafana :3000] end Browser -->|HTTPS| Edge Edge -->|app| Frontend Edge -->|API| Backend Backend -->|CONNECT_SERVICE_URL| Connect Connect -->|S3_ENDPOINT| MinIO Backend -->|DATABASE_URL| PostgreSQL Backend -->|QDRANT_URL| Qdrant Backend -->|S3_ENDPOINT| MinIO Backend -->|OPENAI_BASE_URL| VLLM Backend -.->|OTEL_EXPORTER_OTLP_ENDPOINT| Observability ``` ## Components | Component | Image | Port | Purpose | | -------------- | ------------------------------ | ------------------ | ---------------------------------------------------------------- | | **frontend** | `hellocobi/dashboard-frontend` | 8080 | Vite/React web application | | **backend** | `hellocobi/dashboard-backend` | 3000 | REST API, auth, business logic | | **connect** | `hellocobi/dashboard-connect` | 8000 | Data source ingestion service | | **postgresql** | Bitnami PostgreSQL | 5432 | Auth database (`dashboard_auth`) and core database (`cobi_core`) | | **qdrant** | `qdrant/qdrant` | 6333 / 6334 | Vector store for semantic search | | **vllm-stack** | `vllm/vllm-openai` | 8000 | LLM inference, OpenAI-compatible API | | **minio** | Bitnami MinIO | 9000 / 9001 | S3-compatible object storage for documents | | **otel-lgtm** | `grafana/otel-lgtm` | 3000 / 4317 / 9090 | Grafana + Prometheus + Loki + Tempo | ## Connection Map | From | To | Key environment variable | Port | | ---------------------------------- | ---------------------------------- | --------------------------------------------------------- | ---- | | Browser | Gateway / Ingress / frontend Route | — | 443 | | Gateway / Ingress / frontend Route | frontend | — | 8080 | | Browser | Gateway / Ingress / API Route | `VITE_API_BASE_URL` | 443 | | Gateway / Ingress / API Route | backend | — | 3000 | | Gateway / Ingress | connect | — | 8000 | | backend | connect | `CONNECT_SERVICE_URL` | 8000 | | connect | minio | `S3_ENDPOINT` | 9000 | | backend | postgresql | `DATABASE_URL` | 5432 | | backend | qdrant | `QDRANT_URL` | 6333 | | backend | minio | `S3_ENDPOINT`, `S3_ACCESS_KEY_ID`, `S3_SECRET_ACCESS_KEY` | 9000 | | backend | vllm-stack | `OPENAI_BASE_URL` | 8000 | | backend | otel-lgtm | `OTEL_EXPORTER_OTLP_ENDPOINT` | 4318 | ## Dependency Chart Sources | Chart | Repository | Version | | ------------ | ------------------------------------------------- | -------- | | `postgresql` | `https://charts.bitnami.com/bitnami` | `16.x.x` | | `qdrant` | `https://qdrant.github.io/qdrant-helm` | `1.16.3` | | `vllm-stack` | `https://vllm-project.github.io/production-stack` | `0.1.9` | | `minio` | `https://charts.bitnami.com/bitnami` | `17.x.x` | ## Exposure Models The chart supports Gateway API, Kubernetes Ingress, and OpenShift Routes for the user-facing app/API path. Enable **one** model at a time: * **Gateway API** — uses `Gateway` and `HTTPRoute` resources, tested with Envoy Gateway. This is the preferred kOps v2/self-hosted rehearsal path and is configured under `gateway`. * **Kubernetes Ingress** — uses top-level `ingress` values, tested with `nginx`. It supports shared-host routing and separate hostnames for app, API, Connect, MinIO, and Grafana. * **OpenShift Routes** — uses frontend and backend API `Route` resources with edge TLS termination, configured under `openshift.route`. Connect stays internal through `CONNECT_SERVICE_URL` unless your deployment needs to expose it separately. # PostgreSQL Setup Source: https://docs.hellocobi.com/deployment/postgresql-setup Deploy PostgreSQL in-cluster via the Helm subchart, or connect to an existing standalone database instance ## Overview The backend uses two PostgreSQL databases: `dashboard_auth` for authentication/session data and `cobi_core` for core application data. Set both connection strings in the backend Secret: ``` DATABASE_URL=postgres://:@:5432/dashboard_auth?schema=public CORE_DATABASE_URL=postgres://:@:5432/cobi_core?schema=public ``` Choose one of the two setup options below. *** ## Option A — In-cluster PostgreSQL (Bitnami subchart) The chart bundles the [Bitnami PostgreSQL](https://github.com/bitnami/charts/tree/main/bitnami/postgresql) subchart. Enable it and the chart creates a StatefulSet with a PersistentVolumeClaim. ### Helm values ```yaml theme={null} postgresql: enabled: true image: tag: "16" auth: username: postgres password: "" database: dashboard_auth primary: initdb: scripts: create-core-db.sql: | CREATE DATABASE cobi_core; resources: requests: cpu: 250m memory: 512Mi limits: cpu: "1" memory: 2Gi persistence: enabled: true storageClass: "" # use cluster default; set to "local-path" for on-prem size: 10Gi ``` ### Internal service address Once deployed, PostgreSQL is reachable within the namespace at: ``` -postgresql:5432 ``` ### Backend connection string ```bash theme={null} --from-literal=DATABASE_URL="postgres://postgres:@cobi-dashboard-postgresql:5432/dashboard_auth?schema=public" \ --from-literal=CORE_DATABASE_URL="postgres://postgres:@cobi-dashboard-postgresql:5432/cobi_core?schema=public" ``` ### On-premises StorageClass ```yaml theme={null} postgresql: primary: persistence: storageClass: "local-path" size: 20Gi ``` *** ## Option B — External / standalone PostgreSQL Use this option when you have an existing PostgreSQL instance (on-prem server, managed service, etc.). ### Disable the subchart ```yaml theme={null} postgresql: enabled: false ``` ### Create the database ```sql theme={null} CREATE DATABASE dashboard_auth; CREATE DATABASE cobi_core; GRANT ALL PRIVILEGES ON DATABASE dashboard_auth TO ; GRANT ALL PRIVILEGES ON DATABASE cobi_core TO ; ``` ### Set the backend connection string ```bash theme={null} kubectl create secret generic cobi-backend-secrets \ --namespace cobi \ --from-literal=DATABASE_URL="postgres://:@:/dashboard_auth?schema=public" \ --from-literal=CORE_DATABASE_URL="postgres://:@:/cobi_core?schema=public" \ # ... other variables ``` #### Connection string formats | Scenario | Example | | ------------------------ | ----------------------------------------------------------------------------------------- | | On-prem server (no TLS) | `postgres://cobi:secret@192.168.1.50:5432/dashboard_auth?schema=public` | | On-prem server (TLS) | `postgres://cobi:secret@db.internal:5432/dashboard_auth?schema=public&sslmode=require` | | External managed service | `postgres://cobi:secret@db.example.com:5432/dashboard_auth?schema=public&sslmode=require` | #### SSL mode options | `sslmode` | When to use | | ------------- | ---------------------------------------------------------------- | | `disable` | Internal cluster traffic with no TLS requirement | | `require` | Enforce TLS, skip certificate verification | | `verify-full` | Full CA verification — compliance or internet-facing connections | ### Connecting to an external database from inside the cluster **ExternalName Service** (DNS alias): ```yaml theme={null} apiVersion: v1 kind: Service metadata: name: postgres-external namespace: cobi spec: type: ExternalName externalName: db.internal.example.com ``` Use `postgres-external:5432` as the host in the connection string. **Endpoints object** (IP-based): ```yaml theme={null} apiVersion: v1 kind: Service metadata: name: postgres-external namespace: cobi spec: ports: - port: 5432 --- apiVersion: v1 kind: Endpoints metadata: name: postgres-external namespace: cobi subsets: - addresses: - ip: 192.168.1.50 ports: - port: 5432 ``` *** ## Verify the Connection ```bash theme={null} kubectl logs -n cobi deploy/cobi-dashboard-backend | grep -i "database\|migration\|postgres" ``` A successful startup shows migration completion logs and no `ECONNREFUSED` errors. # Qdrant Setup Source: https://docs.hellocobi.com/deployment/qdrant-setup Configure the Qdrant vector database for semantic search and document embeddings ## Overview Qdrant is the vector database used by Cobi to store and search document embeddings. The `qdrant` dependency chart deploys it as a StatefulSet with persistent storage. **Default ports:** * `6333` — HTTP REST API (used by the backend) * `6334` — gRPC API The backend connects to Qdrant at `http://-qdrant:6333` via the `QDRANT_URL` environment variable. ## Configuration in values.yaml ```yaml theme={null} qdrant: enabled: true image: repository: qdrant/qdrant tag: "v1.16.2" # pin to a specific version — "latest" can break on upgrades pullPolicy: IfNotPresent service: httpPort: 6333 grpcPort: 6334 persistence: enabled: true storageClass: "" # use cluster default; set to "local-path" or "nfs-csi" for on-prem accessModes: - ReadWriteOnce size: 10Gi # increase for large document collections ``` ## Sizing the PVC Qdrant stores vectors on disk. The required space depends on: | Factor | Impact | | ------------------- | ------------------------------------------------ | | Number of documents | Each document generates one or more vectors | | Embedding dimension | Typical embedding models use 384–1536 dimensions | | Index type | HNSW graph adds \~1.5× overhead over raw vectors | A rough estimate: **\~1 Gi per 500 000 vectors** at 768 dimensions with HNSW indexing. Start with `10Gi` and expand the PVC when utilization reaches 70%. ## On-Premises StorageClass For clusters without a cloud storage provisioner, set a local or NFS-backed StorageClass: ```yaml theme={null} qdrant: persistence: storageClass: "local-path" # rancher local-path-provisioner size: 20Gi ``` ## Exposing Qdrant (optional) Qdrant is a ClusterIP service by default (internal only). To expose it for external tooling (e.g., Qdrant Web UI or admin scripts), add an Ingress: ```yaml theme={null} qdrant: ingress: enabled: true ingressClassName: nginx annotations: {} hosts: - host: qdrant.example.com paths: - path: / pathType: Prefix servicePort: 6333 tls: [] ``` Do not expose Qdrant publicly without authentication. The Qdrant HTTP API has no built-in auth by default — restrict access to internal networks or use an ingress that enforces authentication. ## Verify Qdrant ```bash theme={null} # Port-forward the Qdrant HTTP API kubectl port-forward -n cobi svc/cobi-dashboard-qdrant 6333:6333 # Check cluster health curl http://localhost:6333/healthz # List collections curl http://localhost:6333/collections ``` A healthy response from `/healthz` returns `{"title":"qdrant - vector search engine","version":"..."}`. ## Connecting the Backend Set the following variables in the backend Secret. The service name follows the pattern `-qdrant` — if your release is named `cobi-dashboard`, the service is `cobi-dashboard-qdrant`. ### In-cluster Qdrant (deployed via this chart) ```bash theme={null} kubectl create secret generic cobi-backend-secrets \ --namespace cobi \ --from-literal=QDRANT_URL="http://cobi-dashboard-qdrant:6333" \ --from-literal=QDRANT_LOCAL_ONLY="true" \ --from-literal=QDRANT_API_KEY="" \ --from-literal=QDRANT_CLUSTER_KEY="" \ --from-literal=QDRANT_COLLECTION_PREFIX="prod_" \ # ... other keys ``` ### External / standalone on-prem Qdrant If Qdrant is running on a separate server or another cluster: ```bash theme={null} kubectl create secret generic cobi-backend-secrets \ --namespace cobi \ --from-literal=QDRANT_URL="http://qdrant.internal.example.com:6333" \ --from-literal=QDRANT_LOCAL_ONLY="true" \ --from-literal=QDRANT_API_KEY="" \ --from-literal=QDRANT_CLUSTER_KEY="" \ --from-literal=QDRANT_COLLECTION_PREFIX="prod_" \ # ... other keys ``` Keep `QDRANT_LOCAL_ONLY="true"` for any self-hosted Qdrant regardless of where it runs. Set it to `"false"` only when using [Qdrant Cloud](https://cloud.qdrant.io). ### Qdrant Cloud ```bash theme={null} kubectl create secret generic cobi-backend-secrets \ --namespace cobi \ --from-literal=QDRANT_URL="https://.us-east4-0.gcp.cloud.qdrant.io:6333" \ --from-literal=QDRANT_LOCAL_ONLY="false" \ --from-literal=QDRANT_API_KEY="" \ --from-literal=QDRANT_CLUSTER_KEY="" \ --from-literal=QDRANT_COLLECTION_PREFIX="prod_" \ # ... other keys ``` ### Backend environment variable reference | Variable | Required | Description | | -------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------- | | `QDRANT_URL` | Yes | Full HTTP(S) URL of the Qdrant REST API, including port | | `QDRANT_LOCAL_ONLY` | Yes | `"true"` for self-hosted (in-cluster or on-prem); `"false"` for Qdrant Cloud | | `QDRANT_API_KEY` | Cloud only | API key from the Qdrant Cloud console. Leave empty for local | | `QDRANT_CLUSTER_KEY` | Cloud only | Cluster key from the Qdrant Cloud console. Leave empty for local | | `QDRANT_COLLECTION_PREFIX` | No | Prefix prepended to all collection names. Use to namespace multiple environments (e.g. `"prod_"`, `"staging_"`) sharing one Qdrant instance | ## Backup Qdrant supports snapshot-based backups via its REST API: ```bash theme={null} # Create a snapshot of a collection curl -X POST http://localhost:6333/collections//snapshots # List snapshots curl http://localhost:6333/collections//snapshots ``` Snapshots are written to the Qdrant PVC. Copy them to external storage using a CronJob or `kubectl cp`. # Requirements Source: https://docs.hellocobi.com/deployment/requirements Hardware, software, and access requirements for deploying Cobi on-premises ## CLI Tools Install these on the machine you will run Helm from: | Tool | Minimum version | Install | | --------- | --------------- | -------------------------------------------------------- | | `kubectl` | 1.28+ | [kubernetes.io](https://kubernetes.io/docs/tasks/tools/) | | `helm` | 3.12+ | [helm.sh](https://helm.sh/docs/intro/install/) | ## Cluster Requirements ### Kubernetes | Property | Requirement | | ------------------ | --------------------------------------------------- | | Kubernetes version | 1.28+ | | CNI | Any (Calico, Cilium, Flannel, etc.) | | Ingress controller | `ingress-nginx` (IngressClass `nginx`) | | Storage | A default StorageClass with `ReadWriteOnce` support | ### OpenShift | Property | Requirement | | ----------------- | ------------------------------------------------------ | | OpenShift version | 4.12+ | | Ingress | OpenShift Router (built-in) | | Storage | Default StorageClass with `ReadWriteOnce` support | | SCC | `anyuid` SCC for pods that need it (PostgreSQL, MinIO) | ## Node Sizing ### Without GPU inference (vLLM disabled) | Component | CPU request | Memory request | Storage | | ----------------- | ------------ | -------------- | --------------- | | backend | 250m | 512Mi | — | | frontend | 100m | 256Mi | — | | dashboard-connect | 100m | 256Mi | — | | postgresql | 250m | 512Mi | 10 Gi PVC | | qdrant | 500m | 1Gi | 10 Gi PVC | | minio | 250m | 512Mi | 100 Gi PVC | | otel-lgtm | 500m | 1Gi | 25 Gi PVC total | | **Total** | **\~2 vCPU** | **\~5 Gi** | **\~145 Gi** | A single node with **4 vCPU / 8 Gi RAM** and **200 Gi** available disk is sufficient for a minimal deployment. ### With GPU inference (vLLM enabled) The vLLM pod must be scheduled on a GPU node. The recommended model (Qwen3.5-9B-AWQ) requires: | Resource | Minimum | Recommended | | -------------------- | ----------------------------- | ------------------------------------------------------- | | GPU | 1× NVIDIA GPU with 16 Gi VRAM | 1× A10G 24 Gi (e.g. `g5.2xlarge` or on-prem equivalent) | | CPU | 4 vCPU | 8 vCPU | | RAM | 20 Gi | 28 Gi | | Disk (model weights) | 30 Gi | 80 Gi PVC | | NVIDIA driver | 525+ | 535+ | | CUDA | 11.8+ | 12.x | The GPU node must run the **NVIDIA device plugin** DaemonSet so `nvidia.com/gpu` is visible as a schedulable resource. See [GPU Setup](/deployment/gpu-setup). ## Image Registry Access All Cobi application images (`hellocobi/*`) are hosted on Docker Hub as private images. You need: 1. Docker Hub credentials with pull access to the `hellocobi` organization. 2. A Kubernetes Secret of type `kubernetes.io/dockerconfigjson` in the target namespace. ```bash theme={null} kubectl create secret docker-registry dockerhub-secret \ --docker-server=https://index.docker.io/v1/ \ --docker-username= \ --docker-password= \ --docker-email= \ --namespace ``` Reference it in your values file: ```yaml theme={null} global: imagePullSecrets: - dockerhub-secret ``` ## Hugging Face Token vLLM downloads model weights from [huggingface.co](https://huggingface.co) at startup. You need a Hugging Face account and an access token with read access to the model repository: * [Create a token](https://huggingface.co/settings/tokens) with `read` scope. * Pass it via `vllmstack.servingEngineSpec.modelSpec[0].hf_token` in your values file. * For air-gapped clusters, pre-download the model weights and serve them from a local cache volume. ## Persistent Storage All stateful components use `ReadWriteOnce` PersistentVolumeClaims. The default StorageClass is used unless you specify `storageClass` in each component's values. For on-premises clusters without a cloud storage provisioner, common options are: | Provisioner | Notes | | ----------------------- | -------------------------------------------------- | | `rancher.io/local-path` | Single-node dev/staging; data is local to the node | | `nfs.csi.k8s.io` | Multi-node HA; requires an NFS server | | OpenEBS | Block storage for bare-metal clusters | | Longhorn | Distributed block storage for bare-metal clusters | Set the storage class globally per component: ```yaml theme={null} postgresql: primary: persistence: storageClass: "local-path" qdrant: persistence: storageClass: "local-path" minio: persistence: storageClass: "local-path" ``` # vLLM Inference Setup Source: https://docs.hellocobi.com/deployment/vllm-setup Deploy a self-hosted LLM inference endpoint using the vllm-stack subchart ## Overview The `vllmstack` subchart deploys [vLLM](https://github.com/vllm-project/vllm) as an OpenAI-compatible inference server. The backend communicates with it through the `dashboard-connect` service using the standard `/v1/chat/completions` API. The vLLM pod requires a GPU node and is **disabled by default**. Enable it when you have GPU capacity available. ## GPU Node Prerequisite Before enabling vLLM, ensure: 1. A GPU node exists in the cluster with NVIDIA drivers installed. 2. The **NVIDIA device plugin** DaemonSet is running so `nvidia.com/gpu` is a schedulable resource. 3. GPU nodes carry the taint `nvidia.com/gpu:NoSchedule` (the vLLM pod tolerates this automatically when configured as shown below). ```bash theme={null} # Verify GPUs are visible kubectl get nodes -l karpenter.sh/nodepool=gpu-spot kubectl describe node | grep "nvidia.com/gpu" ``` For bare-metal or on-prem clusters, apply the device plugin manually: ```bash theme={null} kubectl apply -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.14.5/nvidia-device-plugin.yml ``` ## Recommended Model — Qwen3.5-9B-AWQ For a single GPU with 16–24 Gi VRAM (e.g., A10G, A100-40G, RTX 4090), the recommended model is **`QuantTrio/Qwen3.5-9B-AWQ`** — an AWQ-quantized 9B parameter model that fits comfortably in 24 Gi VRAM with a 8 192 token context window. | Property | Value | | ---------------------- | ------------------------------ | | Model | `QuantTrio/Qwen3.5-9B-AWQ` | | Quantization | AWQ (4-bit weights) | | VRAM usage | \~9–12 Gi (vs. 18 Gi for BF16) | | Context window | 8 192 tokens | | GPU | 1× A10G 24 Gi (or equivalent) | | Disk (weights + cache) | 80 Gi PVC | ### Values — Qwen3.5-9B-AWQ (recommended) ```yaml theme={null} vllmstack: enabled: true servingEngineSpec: enableEngine: true runtimeClassName: "nvidia" # required for GPU workloads tolerations: - key: "nvidia.com/gpu" operator: "Exists" effect: "NoSchedule" # Target GPU nodes by node pool label (adjust to your cluster labels) # nodeSelector: # karpenter.sh/nodepool: gpu-spot # Karpenter-managed # # OR for on-prem / bare-metal: # node-role/gpu: "true" modelSpec: - name: "qwen35-9b" repository: "vllm/vllm-openai" tag: "latest" modelURL: "QuantTrio/Qwen3.5-9B-AWQ" replicaCount: 1 # Resource requests — size for a g5.2xlarge or equivalent on-prem GPU node requestCPU: 4 requestMemory: "28Gi" requestGPU: 1 limitCPU: "4" limitMemory: "28Gi" shmSize: "2Gi" # shared memory for single-GPU; no NCCL needed pvcStorage: "80Gi" # model weights (~10 Gi AWQ) + HF cache pvcAccessMode: - ReadWriteOnce # Cache directories on the PVC env: - name: HF_HUB_CACHE value: /data/huggingface/hub - name: HUGGINGFACE_HUB_CACHE value: /data/huggingface/hub - name: TRANSFORMERS_CACHE value: /data/huggingface/transformers - name: HF_HUB_DISABLE_XET value: "1" - name: XDG_CACHE_HOME value: /data/.cache - name: TMPDIR value: /data/tmp # Init container creates cache dirs on the PVC before vLLM starts initContainer: name: init-cache-dirs image: busybox:1.36 command: ["sh"] args: - -c - "mkdir -p /data/tmp /data/huggingface/hub /data/huggingface/transformers /data/.cache && chmod -R 777 /data" mountPvcStorage: true vllmConfig: dtype: "float16" tensorParallelSize: 1 # single GPU maxModelLen: 8192 gpuMemoryUtilization: 0.90 enableChunkedPrefill: true enablePrefixCaching: false # disable with AWQ to avoid cache fragmentation maxNumSeqs: 16 extraArgs: - "--trust-remote-code" - "--language-model-only" - "--download-dir" - "/data/huggingface" - "--quantization" - "awq" - "--enforce-eager" - "--attention-backend" - "flashinfer" - "--max-num-batched-tokens" - "1024" - "--disable-log-stats" hf_token: "" routerSpec: enableRouter: false ``` ## Alternative Models ### Qwen3.5-9B (BF16, no quantization) Requires 1× A10G 24 Gi. Slightly lower throughput than AWQ but avoids quantization artifacts. ```yaml theme={null} modelSpec: - name: "qwen35-9b-bf16" modelURL: "Qwen/Qwen3.5-9B" requestGPU: 1 requestMemory: "20Gi" pvcStorage: "30Gi" vllmConfig: dtype: "bfloat16" tensorParallelSize: 1 maxModelLen: 4096 # reduced — BF16 leaves ~4 Gi for KV cache gpuMemoryUtilization: 0.92 enableChunkedPrefill: true enablePrefixCaching: true maxNumSeqs: 16 extraArgs: - "--trust-remote-code" ``` ### Qwen3.5-27B (4× GPU, high quality) Requires 4× A10G 24 Gi (e.g., `g5.12xlarge` or 4-GPU bare-metal node). ```yaml theme={null} modelSpec: - name: "qwen35-27b" modelURL: "Qwen/Qwen3.5-27B" requestCPU: 16 requestMemory: "60Gi" requestGPU: 4 limitCPU: "16" limitMemory: "60Gi" shmSize: "20Gi" # NCCL needs large shared memory for 4-GPU comms pvcStorage: "80Gi" # 27B BF16 weights ≈ 54 Gi vllmConfig: dtype: "bfloat16" tensorParallelSize: 4 # one shard per A10G maxModelLen: 8192 gpuMemoryUtilization: 0.90 enableChunkedPrefill: true enablePrefixCaching: true maxNumSeqs: 32 extraArgs: - "--trust-remote-code" ``` ## Air-Gapped Deployment In environments without internet access, pre-download the model weights to a shared volume or container registry mirror: 1. Download weights on a machine with internet access: ```bash theme={null} pip install huggingface_hub huggingface-cli download QuantTrio/Qwen3.5-9B-AWQ --local-dir ./model-weights ``` 2. Serve the weights from a local HTTP server or a pre-populated PVC. 3. In the values file, set `--download-dir` to the local path and remove the `hf_token`. ## Verify the Inference Endpoint Once the vLLM pod is `Running`: ```bash theme={null} # Port-forward the vLLM service (it is a ClusterIP service) kubectl port-forward -n cobi svc/cobi-dashboard-vllmstack-engine 8000:8000 # Test with a chat completion request curl http://localhost:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "qwen35-9b", "messages": [{"role": "user", "content": "Hello!"}], "max_tokens": 64 }' ``` The service name follows the pattern: `-vllmstack-engine`. ## Configure dashboard-connect Set `LLM_BASE_URL` to point at the vLLM ClusterIP service: ```bash theme={null} kubectl create secret generic cobi-connect-secrets \ --namespace cobi \ --from-literal=LLM_BASE_URL="http://cobi-dashboard-vllmstack-engine:8000/v1" \ --from-literal=LLM_MODEL="qwen35-9b" ``` Then reference it in your values: ```yaml theme={null} connect: envFrom: - secretRef: name: cobi-connect-secrets ``` # Introduction Source: https://docs.hellocobi.com/introduction What is Cobi and how to get started ## What is Cobi? Cobi is an AI-powered data analytics platform that lets you ask questions about your data in plain English, no SQL required. Connect your database and the Data Assistant handles the rest. Connect your database and start chatting with your data instantly Add the Data Assistant to your own website with a single iframe Ask Cobi questions, create charts, and share answers inside Slack Upload customers, transactions, and offers programmatically Surface personalised offers and segments for your users ## How Cobi Works There are two ways to get your data into Cobi. Connecting a database is the fastest way to get started. Point Cobi at an existing PostgreSQL, MySQL, or MongoDB database and the Data Assistant queries it directly in real time. If you need programmatic control or don't have a directly queryable database, you can upload data via the API instead, pushing structured records like customers, transactions, and offers to Cobi's hosted data platform. ## Getting Started [Connect a database](/platform/data-connectors/overview) or [upload data via API](/quickstart). Add [Custom Instructions](/platform/custom-instructions) so the Data Assistant understands your terminology and business rules. Open the Data Assistant and ask questions about your data in plain English. [Connect Slack](/platform/channels/slack/overview) or [embed the chat widget](/platform/channels/embed-chat-widget) so your team can explore data where they work. Need help? Contact us at [support@hellocobi.com](mailto:support@hellocobi.com). # Data Scopes Source: https://docs.hellocobi.com/platform/channels/data-scopes Restrict which tables an embed key can query and enforce row-level isolation for multi-tenant deployments ## Overview A **data scope** defines the exact slice of your database that an embed key is allowed to access. Instead of granting an embed key unrestricted access to every table in your data source, you create a named scope that specifies: * Which **tables** the key may query * Which **row-level filter** should be applied so that each caller only sees their own rows Scopes are created independently of embed keys and then assigned to a key during key creation. One scope can be shared across many keys. A `scopeId` URL parameter is required whenever an embed key has a data scope assigned to it. Requests without a valid `scopeId` will be rejected with a `403` error. ## Creating a Scope Navigate to **Developer** > **Channels** and click **Create Scope** (or open the Scopes tab if visible). Provide a **Name** (required) and an optional **Description**. | Field | Required | Description | | --------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | **Name** | Yes | A short, human-readable label used to identify the scope when assigning it to embed keys (e.g. `Customer read-only`, `Outlet dashboard`) | | **Description** | No | Free-text note that helps team members understand what data this scope exposes | Click **Next** when done. This step has two parts: **Row-level scope** and **Table access**. ### Row-level scope Row-level scoping restricts each caller to only the rows that belong to them. It is required for multi-tenant deployments where different end-users must not see each other's data. | Setting | Description | | ---------------- | ------------------------------------------------------------------------ | | **Scope table** | The table that owns the tenant identifier (e.g. `outlets`) | | **Scope column** | The primary column in that table that identifies each tenant (e.g. `id`) | At query time, Cobi automatically filters every query so that only rows matching the caller's `scopeId` value are returned. If all callers should see the same data (e.g. a public read-only widget), leave row-level scoping unconfigured and rely on table access restrictions alone. ### Table access Select which tables this scope can query. Tables are grouped to help you make the right choices: | Group | Description | | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Related tables** | Tables that contain a foreign key referencing the scope column. These are typically the safest to include because row-level filtering applies to them automatically | | **Other tables** | Tables without a direct relationship to the scope column. Queries against these tables will not be row-filtered, so include them only when full-table access is intentional | Use the **Filter tables** search box to find specific tables quickly. Click **Next** when done. The **Review scope** screen summarises your configuration: | Field | Value | | ------------- | ------------------------------------------------------------------------------------------------- | | **Name** | The scope name you entered | | **Tables** | Number of tables included | | **Row scope** | The `table.column` pair used for filtering, and a note that it is *filtered by caller's scope ID* | Click **Create scope** to save. The scope is immediately available for assignment to embed keys. ## Assigning a Scope to an Embed Key When creating or editing an embed key, the **Security** step includes a **Data scope** dropdown: | Option | Behaviour | | -------------------------- | ------------------------------------------------------------------------------------------------------------------ | | **Full access (no scope)** | The key can query all tables enabled on the data source. No `scopeId` parameter is required or used | | **A named scope** | The key can only query the tables and rows defined in that scope. A `scopeId` must be supplied in every iframe URL | Selecting a scope on an embed key is permanent for that key. To change the scope, create a new embed key with the desired scope and revoke the old one. ## Using scopeId in the Iframe URL When an embed key has a data scope assigned, every iframe `src` must include the `scopeId` parameter. The value should be the identifier of the tenant or entity whose data the current user is allowed to see — typically the primary key value from the scope table. ```html theme={null} ``` Cobi uses `scopeId` to filter all queries at the row level. In the example above, queries against the `outlets` scope table would automatically include `WHERE outlets.id = 'outlet_42'`, and all related table queries would be filtered accordingly. Render the `scopeId` server-side from your own authentication system. Never derive it from user-supplied input or client-side storage, as this would allow a user to spoof another tenant's ID. ### What happens without scopeId If a request is made to an embed key that has a scope assigned but no `scopeId` is provided, Cobi returns a `403` error and the widget displays an access-denied message. This is intentional — a missing `scopeId` is treated as an unauthorised request. ## Managing Scopes From **Developer > Channels** (Scopes tab) you can view all scopes along with the number of tables and their row-scope configuration. | Action | How | | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **View** | Click on a scope to see its full configuration | | **Delete** | Open the scope and click **Delete**. Any embed key currently using this scope will lose its data access restrictions — review key assignments before deleting | Deleting a scope does not automatically revoke the embed keys assigned to it. Those keys will revert to having no scope, potentially exposing more data than intended. Audit your embed keys after any scope deletion. ## Security Considerations * **Scope isolation** — each embed key can only be assigned one scope. There is no way for a key to escalate beyond its assigned scope at runtime. * **Row-level enforcement** — Cobi enforces row-level filtering server-side. It cannot be bypassed by modifying the iframe URL or the chat query. * **scopeId is not a secret** — the `scopeId` value is visible in the iframe `src`. It should only identify *which tenant*, not authenticate the request. Authentication is handled by the embed key itself. Always combine `scopeId` with domain restrictions (allowed origins) in production. * **Minimal table access** — follow the principle of least privilege: only include the tables your widget actually needs. Excluding sensitive tables from the scope is the safest way to prevent unintended data exposure. # Chat Widget Source: https://docs.hellocobi.com/platform/channels/embed-chat-widget Add Cobi's Data Assistant to your website with a simple iframe ## Overview The Cobi chat widget can be embedded on any website as an iframe. Visitors interact with your AI assistant directly on your site. Authentication is handled via embed API keys that you create in the Cobi dashboard. Embed keys are scoped to a single data source and can be restricted to specific domains for production use. ## Getting Started Navigate to **Developer** > **Channels** in the Cobi dashboard. Click **Create Embed Key** and fill in: | Field | Description | | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | | **Name** | A label for this key (e.g. "Production website") | | **Data Source** | The data source the embed will query against | | **Data Scope** *(optional)* | Restrict which tables the key can query and enforce row-level isolation. See [Data Scopes](/platform/channels/data-scopes) | | **Allowed Origins** *(optional)* | Domains permitted to load the widget (e.g. `https://yoursite.com`) | | **Expiry Date** *(optional)* | When the key should expire. Leave blank for no expiry | | **Chat History** *(optional)* | Allow this key to access conversation history. Must be enabled here before the `chatHistory` URL parameter has any effect | The full API key is only shown once at creation time. Copy the embed snippet immediately. You won't be able to retrieve the full key later. After creating the key, you'll see an embed snippet. Copy and paste it into your website's HTML: ```html theme={null} ``` Load your page in a browser. The chat widget should appear and respond to messages using your configured data source. ## Configuration ### URL Parameters The embed URL supports the following query parameters: | Parameter | Required | Description | | ------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `key` | Yes | Your embed API key | | `scopeId` | Conditional | The tenant or entity identifier used for row-level filtering. Required when the embed key has a [data scope](/platform/channels/data-scopes) assigned. Requests without it return `403` | | `theme` | No | `light`, `dark`, or `system` (default). Matches the widget's appearance to your site | | `chatHistory` | No | `true` to show the chat history panel. Only takes effect if history was enabled when the embed key was created | | `userId` | No | An identifier for the current user. See [User Identity](#user-identity) below | ``` https://app.hellocobi.com/embed/chat?key=cobi_embed_...&scopeId=outlet_42&theme=dark&chatHistory=true&userId=user_123 ``` ### Allowed Origins When you specify allowed origins during key creation, only those domains can load the widget. Requests from other origins are rejected with a `403` error. * Leave empty during development to allow any origin * For production, always specify your exact domains (e.g. `https://yoursite.com`) * The `Origin` header is required when allowed origins are configured You can add multiple allowed origins to a single key, for example if your site is accessible on both `https://example.com` and `https://www.example.com`. ### Theming The widget respects light and dark mode via the `theme` parameter: | Value | Behavior | | -------- | ------------------------------------------ | | `system` | Matches the user's OS preference (default) | | `light` | Forces light mode | | `dark` | Forces dark mode | ### Chat History Chat history lets users return to previous conversations in the widget. It is controlled by two things: 1. **Key setting:** History must be enabled when creating the embed key in the dashboard. If it wasn't, passing `chatHistory=true` in the URL has no effect. 2. **URL parameter:** Even if the key supports history, you must pass `chatHistory=true` to make the history panel visible in the iframe. This two-layer control lets you enable history on a key but selectively show it only on certain pages. ### User Identity By default, the widget generates a unique user ID per browser and stores it in `localStorage`. A returning visitor on the same browser is recognised as the same user and can see their previous chats, but the same user on a different device starts fresh. To share chat history across browsers, pass your own `userId`: ```html theme={null} ``` Pass a stable, per-user identifier from your own auth system (e.g. a database user ID). Do not use personally identifiable information such as email addresses as the `userId`. ### Data Scoping Data scopes let you restrict an embed key to a specific subset of tables and enforce row-level isolation so each caller only sees their own data. This is the recommended security model for any multi-tenant deployment. When a scope is assigned to an embed key, you must pass a `scopeId` in every iframe URL. The value is the primary key of the tenant or entity whose data the current user should see: ```html theme={null} ``` Always render `scopeId` server-side from your own authentication system. Never derive it from user input or client-side storage, as this would allow a user to access another tenant's data. See [Data Scopes](/platform/channels/data-scopes) for a full walkthrough on creating scopes and configuring row-level access. ## Available Connections The embed widget queries whichever data source you select when creating the embed key. All data source types supported by Cobi are available: | Type | Notes | | -------------- | ---------------------------------------------------------------------------------------------------------------- | | **PostgreSQL** | PostgreSQL 12+ | | **MySQL** | MySQL 8.0+ | | **MongoDB** | MongoDB Atlas and self-hosted instances. See [MongoDB](/platform/data-connectors/mongodb) for schema conventions | The data source is locked to the key at creation time and cannot be changed by the client. ## Rate Limiting Each embed key is rate-limited to **1,000 requests per hour**. When the limit is exceeded: * The API returns `429 Too Many Requests` with a `Retry-After` header * The widget displays a user-friendly message asking the visitor to wait If you need higher limits, contact the Cobi team at [support@hellocobi.com](mailto:support@hellocobi.com). ## Security Keys are hashed at rest and only the first 18 characters are stored for display. When allowed origins are configured, requests from any other domain are rejected outright. The data source is locked to the key at creation time and cannot be changed by the client. Embed requests have restricted access and cannot view internal documents. The widget does not use cookies or require user authentication. When a [data scope](/platform/channels/data-scopes) is assigned to a key, table access and row-level filtering are enforced server-side and cannot be bypassed by modifying the iframe URL. The `scopeId` parameter identifies the caller's tenant but does not authenticate the request — authentication is always handled by the embed key itself. ## Managing Keys From **Developer > Channels** you can view all active embed keys along with their allowed origins and status, and permanently revoke any key. When a key is deleted it takes effect on the next request with no delay or grace period. ## Troubleshooting | Issue | Cause | Solution | | ------------------------------ | ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | | Widget shows "Missing API Key" | No `key` parameter in the URL | Check the iframe `src` includes `?key=cobi_embed_...` | | `403` error — access denied | Origin not in allowed list, or `scopeId` missing/invalid for a scoped key | Add your domain to the key's allowed origins; if the key has a data scope, ensure `scopeId` is present in the URL and matches a valid tenant ID | | `401` error | Key is invalid, expired, or deleted | Create a new embed key in Developer > Channels | | `429` error | Rate limit exceeded | Wait for the rate limit window to reset (1 hour) | | Widget crashes / white screen | JavaScript error | The widget includes an error boundary. Try refreshing. If it persists, check the browser console | # Account Linking Source: https://docs.hellocobi.com/platform/channels/slack/account-linking Connect your Slack user to your Cobi account Account linking connects your Slack user to your Cobi user. This improves attribution and makes it clear who asked each question. ## How linking works A Cobi admin must connect the Slack workspace from **Settings** > **Integrations** first. In Slack, run `/cobi link`. Click **Link account** in the private Slack response. Finish linking while signed in to the Cobi account that belongs to the connected organization. Run `/cobi status` to confirm your account is linked. ## Linked users Linked users are attributed to their Cobi user. This is useful for audit trails, answer ownership, and support. ## Unlinked users Unlinked users can still ask Cobi questions in a connected Slack workspace. Their questions are attributed to their Slack user instead of a Cobi user. ## External guests External guests cannot use Cobi in Slack. If an external guest mentions Cobi, Cobi rejects the request. ## Common linking issues | Issue | What to do | | ---------------------------- | --------------------------------------------------------------- | | `Slack is not connected` | Ask a Cobi admin to install Slack from Cobi settings | | Link expired | Run `/cobi link` again | | Link already used | Run `/cobi link` again to create a new link | | Wrong Cobi organization | Switch to a Cobi user in the connected organization, then retry | | `/cobi status` says unlinked | Complete `/cobi link` in Cobi | You only need to link once per connected Slack workspace. # Install Slack Source: https://docs.hellocobi.com/platform/channels/slack/install Connect a Slack workspace to a Cobi organization Install Slack from Cobi so people in your workspace can ask the Data Assistant questions without leaving Slack. ## Prerequisites * You are a Cobi admin. * You have permission to install apps in Slack. * Your Cobi organization has at least one connected data source. A Slack workspace can be connected to one Cobi organization at a time. This keeps Slack questions routed to the correct data sources and permissions. ## Connect Slack In Cobi, go to **Settings**. Select the **Integrations** tab. Find the Slack card and click **Connect**. Review the Slack authorization screen and approve the install. After Slack redirects back to Cobi, the Slack card shows the connected workspace. In Slack, invite Cobi to every channel where people should ask questions. ## Reconnect Slack Use **Reconnect** from the Slack card if the app was reinstalled in Slack or needs refreshed access. Reconnecting keeps the workspace mapped to the same Cobi organization. ## Disconnect Slack Use **Disconnect** from the Slack card to remove the workspace connection from Cobi. After disconnecting, Cobi stops answering questions from that Slack workspace until an admin connects it again. If a workspace is already connected to a different Cobi organization, it cannot be claimed by another organization. Disconnect it from the first organization before connecting it somewhere else. ## What users do next After installation, users can: * Mention Cobi in invited channels. * DM Cobi. * Run `/cobi help` to see commands. * Run `/cobi link` to connect their Slack user to their Cobi account. # Slack Source: https://docs.hellocobi.com/platform/channels/slack/overview Use Cobi inside Slack to ask questions, create charts, and share answers with your team Cobi brings the Data Assistant into Slack so your team can ask questions where work is already happening. Ask in a channel, DM the app, use `/cobi`, or continue a Cobi thread. Mention Cobi in channels where the app has been invited Run `/cobi ask`, `/cobi chart`, `/cobi table`, `/cobi link`, and `/cobi status` Connect your Slack user to your Cobi user for attribution Fix common setup, linking, and response issues ## The main ways to use Cobi ### Mention Cobi Mention Cobi in a channel to ask a question: ```text theme={null} @Cobi how is revenue trending this month? ``` Cobi replies in a thread so the answer stays close to the original question. ### DM Cobi Send Cobi a direct message for questions that do not need to be visible in a channel. ```text theme={null} hello cobi ``` ### Use `/cobi` Use slash commands when you want a specific flow: ```text theme={null} /cobi chart revenue by week /cobi table top customers /cobi status ``` Commands like `help`, `status`, and `link` are private to you. Commands that ask for analysis create a public thread in the channel where you ran them. ### Follow up in threads After Cobi answers in a thread, you can keep replying there without mentioning Cobi again. ```text theme={null} go ahead show that by month explain the rows above ``` Cobi uses the prior thread context so follow-ups stay connected. ## What Cobi can return * Short answers with the key takeaway. * Chart images for trend or comparison questions. * Table or CSV artifacts for ranking, list, or record-level questions. * An `Open Cobi` button when the full answer is easier to inspect in the web app. * Feedback tracking when you react to an answer with thumbs up or thumbs down. ## Before you start Ask a Cobi admin to [connect your Slack workspace](/platform/channels/slack/install). Invite Cobi to each channel where people should ask questions. Run [`/cobi link`](/platform/channels/slack/account-linking) if you want Slack activity attributed to your Cobi user. Cobi is designed to stay quiet in normal channel conversation. It responds when mentioned, DMed, invoked with `/cobi`, or when you reply inside a Cobi-owned thread. # Slash Commands Source: https://docs.hellocobi.com/platform/channels/slack/slash-commands Reference for the /cobi command in Slack Cobi uses one Slack command: `/cobi`. Use subcommands to link your account, check status, ask a question, create a chart, or create a table. ## Quick reference | Command | Visibility | What it does | | ------------------------ | ------------- | --------------------------------------------------------------------------- | | `/cobi help` | Private | Shows the available commands | | `/cobi status` | Private | Shows whether the workspace is connected and whether your account is linked | | `/cobi link` | Private | Creates a one-time account link | | `/cobi ask ` | Public thread | Starts an answer thread in the current channel | | `/cobi chart ` | Public thread | Starts an answer thread and asks Cobi for a chart | | `/cobi table ` | Public thread | Starts an answer thread and asks Cobi for a table or CSV | ## `/cobi help` Shows a concise command reference in Slack. ```text theme={null} /cobi help ``` Use this when you forget the command names. ## `/cobi status` Shows whether the Slack workspace is connected to Cobi and whether your Slack user is linked to your Cobi account. ```text theme={null} /cobi status ``` The response is private to you. ## `/cobi link` Creates a one-time link that connects your Slack user to your Cobi user. ```text theme={null} /cobi link ``` The response is private to you and includes a **Link account** button. The link is valid for 15 minutes. ## `/cobi ask ` Starts a public thread in the current channel and answers your question there. ```text theme={null} /cobi ask how is revenue trending this month? ``` Use this when you want the channel to see the answer. ## `/cobi chart ` Starts a public thread and asks Cobi to produce a chart when the data supports it. ```text theme={null} /cobi chart revenue by week ``` Cobi posts the answer and attaches a chart image when a chart is available. ## `/cobi table ` Starts a public thread and asks Cobi to produce a table or CSV when the data supports it. ```text theme={null} /cobi table top customers by revenue this quarter ``` Cobi posts the answer and attaches a table or CSV when there are rows to show. `help`, `status`, and `link` are private responses. `ask`, `chart`, and `table` create public Slack threads in the channel where you run them. # Slack Troubleshooting Source: https://docs.hellocobi.com/platform/channels/slack/troubleshooting Fix common Cobi Slack setup and usage issues Use this guide when Cobi does not answer, linking fails, or an artifact does not appear. ## Cobi does not respond in a channel Check these first: * Cobi was invited to the channel. * Your message mentioned Cobi, used `/cobi`, or was inside a Cobi-owned thread. * The Slack workspace is connected in Cobi settings. * You are not using Slack as an external guest. If Cobi still does not respond, ask a Cobi admin to reconnect Slack from **Settings** > **Integrations**. ## Cobi is silent in normal channel chatter That is expected. Cobi only responds when: * You mention Cobi. * You DM Cobi. * You run `/cobi`. * You reply in a thread where Cobi already answered. This keeps the bot from interrupting unrelated conversations. ## `/cobi link` says Slack is not installed Slack must be connected from Cobi before account linking works. Ask a Cobi admin to go to **Settings** > **Integrations** > **Slack** and connect the workspace. ## `/cobi status` says your account is unlinked Run: ```text theme={null} /cobi link ``` Then click **Link account** and finish the flow in Cobi. Run `/cobi status` again after linking. ## Your link expired Account links are short-lived. Run `/cobi link` again to get a fresh link. ## Cobi answered but the chart or table is missing Cobi keeps the answer visible even if an artifact cannot be attached. Try: * Asking for a narrower date range. * Asking for fewer rows. * Asking for a chart or table explicitly. * Opening the answer in Cobi if the button is available. If the issue continues, contact [support@hellocobi.com](mailto:support@hellocobi.com) with the channel, approximate time, and question text. ## Cobi asks what records you mean Reply inside the same Cobi thread where the table or chart appeared. Cobi uses thread context only inside Cobi-owned threads. ## Cobi says it cannot access the workspace The workspace may not be connected to the Cobi organization, or it may already be connected to a different Cobi organization. Ask a Cobi admin to check the Slack card in **Settings** > **Integrations**. ## Still stuck Contact [support@hellocobi.com](mailto:support@hellocobi.com). Include: * The Slack workspace name. * The channel or DM where the issue happened. * The command or message you sent. * The approximate time. # Using Cobi in Slack Source: https://docs.hellocobi.com/platform/channels/slack/using-cobi Ask Cobi questions in channels, DMs, and threads Once Slack is connected, Cobi works like a teammate in Slack. Ask a clear data question, then continue in the thread if you want to refine the answer. ## Ask in a channel Mention Cobi in a channel where the app has been invited: ```text theme={null} @Cobi how did revenue trend over the last 6 months? ``` Cobi replies in the same thread. This keeps the channel readable and keeps follow-ups attached to the original question. ## Ask in a DM Open a direct message with Cobi and ask normally: ```text theme={null} which products are driving the most revenue this quarter? ``` DMs are useful for quick exploration before you share a result with a channel. ## Follow up in a Cobi thread Inside a thread where Cobi has already answered, reply without mentioning Cobi: ```text theme={null} break that down by month show the top customers why did September drop? ``` Cobi uses the thread context, including earlier answers and visible Cobi tables, to understand what you mean. ## Ask for a chart Use natural language or the chart command: ```text theme={null} @Cobi chart revenue by week /cobi chart revenue by week ``` Cobi posts the answer and attaches a chart image when a chart is useful. ## Ask for a table Use tables for rankings, lists, and record-level results: ```text theme={null} @Cobi table top customers by revenue this quarter /cobi table top customers by revenue this quarter ``` Cobi posts a Slack-readable answer and attaches a table or CSV artifact when there are rows to show. ## Open the full answer in Cobi Some answers include an **Open Cobi** button. Use it when you want a larger view in the web app. ## Give feedback React to a Cobi answer with thumbs up or thumbs down. This helps your team track answer quality and improve the assistant over time. ## When Cobi responds Cobi responds when: * You mention Cobi in a channel. * You DM Cobi. * You run a `/cobi` command. * You reply in a thread where Cobi already answered. Cobi stays silent in normal channel chatter outside those cases. # Custom Instructions Source: https://docs.hellocobi.com/platform/custom-instructions Add context and business rules to help Cobi understand your data ## Overview Custom Instructions allow you to provide context, terminology definitions, and business rules that help the Data Assistant understand your specific data model and give more accurate responses. ## Why Use Custom Instructions? Every organization has unique terminology, data structures, and business logic. Custom Instructions bridge the gap between your internal knowledge and Cobi's understanding of your data. Map your business terms to database tables and columns Clarify how tables connect and what joins are appropriate Define how to calculate metrics like revenue, active users, or churn Help Cobi write correct queries on the first try ## Adding Custom Instructions Navigate to **Developer** > **Data Connectors** in the sidebar. Click on the data source you want to configure. In the connection modal, click the **Custom Instructions** tab. Enter your instructions in plain text. Be specific and comprehensive. Click **Save Changes** to apply your instructions. ## What to Include ### Business Context Start with a high-level description of what your database contains: ```text theme={null} This is a sports facility management database for a tennis club. ``` ### Key Terminology Define terms your team uses and map them to database concepts: ```text theme={null} Key terminology: - "Members" = users with active package subscriptions - "Courts" = playing facilities (can have sub-courts) - "Bookings" = court reservations - "Lessons" = private coaching sessions (instructor_bookings table) - "Programs" = group classes, camps, clinics ``` ### Revenue Sources Explain where different metrics come from: ```text theme={null} Revenue comes from: 1. Court bookings (bookings table) 2. Private lessons (instructor_bookings table) 3. Program enrollments (program_enrollments table) 4. Membership fees (via Stripe subscriptions) ``` ### Query Guidance Provide specific instructions for common questions: ```text theme={null} When asked about "revenue", sum the `total` field from bookings and instructor_bookings. When asked about "members", count users with active package_user_mappings. Payment status 1 = paid, 0 = unpaid. Booking status 1 = confirmed, 0 = pending, 2 = cancelled. ``` ## Example: Complete Custom Instructions Here's a full example for a sports facility: ```text theme={null} This is a sports facility management database for a Padel/Tennis club. Key terminology: - "Members" = users with active package subscriptions - "Courts" = playing facilities (can have sub-courts) - "Bookings" = court reservations - "Lessons" = private coaching sessions (instructor_bookings table) - "Programs" = group classes, camps, clinics Revenue comes from: 1. Court bookings (bookings table) 2. Private lessons (instructor_bookings table) 3. Program enrollments (program_enrollments table) 4. Membership fees (via Stripe subscriptions) When asked about "revenue", sum the `total` field from bookings and instructor_bookings. When asked about "members", count users with active package_user_mappings. Payment status 1 = paid, 0 = unpaid. Booking status 1 = confirmed, 0 = pending, 2 = cancelled. ``` ## Best Practices ### Be Specific Instead of "customers are in the users table", say "customers are stored in the `users` table, identified by the `id` column, with `user_type = 'customer'` to distinguish from staff accounts." ### Document Status Codes Many databases use numeric codes for statuses. Always document what they mean: ```text theme={null} Order status codes: - 0 = draft - 1 = pending payment - 2 = paid - 3 = shipped - 4 = delivered - 5 = cancelled ``` ### Explain Calculations If metrics require specific calculations, be explicit: ```text theme={null} "Active users" = users who have logged in within the last 30 days (check `last_login` column). "Monthly revenue" = SUM(amount) from transactions WHERE status = 'completed' AND refunded = false. ``` ### Note Table Relationships Help Cobi understand how to join tables correctly: ```text theme={null} To get customer orders: JOIN customers ON orders.customer_id = customers.id To get order items: JOIN order_items ON order_items.order_id = orders.id Products are linked via order_items.product_id = products.id ``` ### Update Regularly As your data model evolves, update your Custom Instructions to reflect new tables, columns, or business rules. ## Learned Information In addition to Custom Instructions (which you write), Cobi automatically builds **Learned Information** as you interact with the Data Assistant. This includes: * Table and column descriptions discovered through queries * Relationships between tables * Common query patterns You can view and edit Learned Information in the **Learned Information** tab of your data connector settings. ## Troubleshooting ### Data Assistant Still Gets It Wrong If Cobi isn't interpreting your data correctly: 1. Check your Custom Instructions for clarity 2. Add more specific examples 3. Correct any Learned Information that may be inaccurate 4. Try rephrasing your question to include the terminology you defined ### Instructions Not Taking Effect After saving Custom Instructions: * Start a new conversation to ensure the context is loaded * Check that you're using the correct data source in your query ## Next Steps * [Connect your database](/platform/data-connectors/overview) if you haven't already * Start asking questions in the Data Assistant to see your instructions in action # MongoDB Source: https://docs.hellocobi.com/platform/data-connectors/mongodb Connect a MongoDB database to Cobi ## Connecting to MongoDB Atlas MongoDB Atlas uses SRV connection strings. Enter your Atlas cluster hostname (e.g. `cluster0.abc123.mongodb.net`) as the host. Cobi detects the SRV pattern automatically and handles the port for you. ## Connecting to a Self-Hosted MongoDB Enter your server's hostname or IP address and port (`27017` by default). ## Schema Conventions Cobi maps MongoDB collections to SQL tables. Keep the following in mind when chatting with the Data Assistant: | Convention | Detail | | ----------------- | ----------------------------------------------------------------------------------- | | **Collections** | Each collection appears as a table | | **`_id` field** | Present on every document as a unique identifier, used as the primary key | | **Nested fields** | Flattened with underscores, so `address.city` becomes `address_city` | | **Arrays** | Array fields are queryable. Ask the Data Assistant to expand or filter them | | **Relationships** | MongoDB has no foreign keys. Reference related collections by matching field values | ## Create a Read-Only User **Self-hosted MongoDB:** run the following in `mongosh`: ```js theme={null} use admin db.createUser({ user: "cobi_reader", pwd: "secure_password", roles: [{ role: "read", db: "your_database" }] }) ``` **MongoDB Atlas:** create the user in the Atlas UI under **Database Access**, with the built-in `read` role scoped to your database. ## Troubleshooting See the [Troubleshooting](/platform/data-connectors/troubleshooting) page for help with connection issues. # MySQL Source: https://docs.hellocobi.com/platform/data-connectors/mysql Connect a MySQL database to Cobi ## Best Practices ### Create a Read-Only User We strongly recommend creating a dedicated database user with read-only permissions: ```sql theme={null} CREATE USER 'cobi_reader'@'%' IDENTIFIED BY 'secure_password'; GRANT SELECT ON your_database.* TO 'cobi_reader'@'%'; FLUSH PRIVILEGES; ``` ### Limit Table Access If you only want Cobi to access specific tables, grant permissions selectively: ```sql theme={null} -- Grant access to specific tables only GRANT SELECT ON your_database.customers TO 'cobi_reader'@'%'; GRANT SELECT ON your_database.transactions TO 'cobi_reader'@'%'; FLUSH PRIVILEGES; ``` ## Troubleshooting See the [Troubleshooting](/platform/data-connectors/troubleshooting) page for help with connection issues. # Overview Source: https://docs.hellocobi.com/platform/data-connectors/overview Connect your database to start exploring your data with Cobi ## Overview Data Connectors allow you to link your existing database directly to Cobi, enabling the Data Assistant to query and analyze your data in real-time. All database connections are **read-only**. Cobi will never modify your data. ## Supported Databases Cobi currently supports the following database types: * [**PostgreSQL**](/platform/data-connectors/postgresql) (versions 12 and above) * [**MySQL**](/platform/data-connectors/mysql) (version 8.0 and above) * [**MongoDB**](/platform/data-connectors/mongodb) (Atlas via SRV connection strings and self-hosted instances) ## Connecting Your Database Navigate to the **Developer** section in the sidebar, then select **Data Connectors**. Click the **Add Data Source** button to open the connection form. Fill in your database credentials: | Field | Description | | ------------------- | --------------------------------------------------------------------------------- | | **Connection Name** | A friendly name to identify this data source (e.g., "Production DB", "Analytics") | | **Host** | Your database server address (e.g., `db.example.com` or an IP address) | | **Port** | The database port (default: `5432` for PostgreSQL) | | **Database Name** | The name of the specific database to connect to | | **Username** | Database user with read access | | **Password** | Password for the database user | Click **Test Connection** to verify your credentials. Cobi will attempt to connect and confirm access. Once the test succeeds, click **Save Changes** to store your connection. ## Network Configuration ### IP Allowlisting If your database is behind a firewall, you'll need to allowlist Cobi's IP addresses: ``` xx.xx.xxx.xx xx.xx.xxx.xx ``` Contact [support@hellocobi.com](mailto:support@hellocobi.com) to get the IP addresses for your region. ### Using SSH Tunnels For databases that aren't publicly accessible, we recommend setting up an SSH tunnel or using a bastion host. Contact our support team for guidance on secure connection options. ## Managing Connections ### Edit a Connection To update connection details: 1. Go to **Developer** > **Data Connectors** 2. Click on the connection you want to edit 3. Update the fields as needed 4. Click **Test Connection** to verify, then **Save Changes** ### Delete a Connection To remove a data source: 1. Go to **Developer** > **Data Connectors** 2. Click on the connection 3. Click **Delete Connection** Deleting a connection will remove all learned information and custom instructions associated with that data source. ## Next Steps Once your database is connected: * Add [Custom Instructions](/platform/custom-instructions) to help Cobi understand your data * Start a conversation with the Data Assistant to explore your data # PostgreSQL Source: https://docs.hellocobi.com/platform/data-connectors/postgresql Connect a PostgreSQL database to Cobi ## Best Practices ### Create a Read-Only User We strongly recommend creating a dedicated database user with read-only permissions: ```sql theme={null} CREATE USER cobi_reader WITH PASSWORD 'secure_password'; GRANT CONNECT ON DATABASE your_database TO cobi_reader; GRANT USAGE ON SCHEMA public TO cobi_reader; GRANT SELECT ON ALL TABLES IN SCHEMA public TO cobi_reader; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO cobi_reader; ``` ### Limit Table Access If you only want Cobi to access specific tables, grant permissions selectively: ```sql theme={null} -- Grant access to specific tables only GRANT SELECT ON customers, transactions, orders TO cobi_reader; ``` ## Troubleshooting See the [Troubleshooting](/platform/data-connectors/troubleshooting) page for help with connection issues. # Troubleshooting Source: https://docs.hellocobi.com/platform/data-connectors/troubleshooting Diagnose and fix common Data Connector issues ## Connection Failed If your connection test fails, work through the following checks: 1. **Firewall rules.** Make sure Cobi's IP addresses are allowlisted. 2. **Credentials.** Double-check your username and password are correct. 3. **Host and port.** Confirm the host address and port match your database configuration. 4. **Database name.** Check that the database exists and the user has been granted access to it. 5. **SSL requirements.** Some databases require SSL connections. 6. **MongoDB only.** Ensure outbound HTTPS access is permitted from your server. Contact [support@hellocobi.com](mailto:support@hellocobi.com) for on-premises or air-gapped deployments. ## Slow Queries If the Data Assistant is slow when querying your database: 1. Ensure your database has appropriate indexes on the columns being queried. 2. Consider creating views for commonly queried data. 3. Add [Custom Instructions](/platform/custom-instructions) to help Cobi write more targeted queries. # Quickstart Source: https://docs.hellocobi.com/quickstart Make your first API call to the Cobi Data API ## Prerequisites * An API key (contact [support@hellocobi.com](mailto:support@hellocobi.com) to request one) * A tool to make HTTP requests (cURL, Postman, or your preferred language) ## Your First API Call Upload a customer record to get started: ```bash theme={null} curl -X POST https://api.hellocobi.com/v1/data/customers \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "records": [ { "customer_id": "550e8400-e29b-41d4-a716-446655440000", "user_id": "550e8400-e29b-41d4-a716-446655440001", "region": "US", "birthdate": "1990-01-01", "gender": "MALE" } ] }' ``` A successful response: ```json theme={null} { "status": "success", "message": "Records uploaded successfully", "count": 1 } ``` ## Available Endpoints Once you're uploading successfully, explore the full set of endpoints: * [Customers](/api-reference/data/endpoint/customers) * [Transactions](/api-reference/data/endpoint/transactions) * [Offers](/api-reference/data/endpoint/offers) * [Shops, branches, categories, and more](/api-reference/data/overview) ## Best Practices Send up to 5,000 records per request for the best throughput. Before uploading at scale, check your payload against the [data models](/api-reference/data/overview) to catch schema errors early. Always check the response status on each request and retry with exponential backoff on `429` or `5xx` responses. ## Next Steps * Read the [Authentication](/authentication) guide for API key security and error codes * Browse the [Data Models](/api-reference/data/overview) to see required and optional fields for each record type * Check out the [Recommendations API](/api-reference/recommendations/overview) to surface personalised offers for your users