ks.today API Documentation

1. Introduction

This guide is designed to help you integrate your own applications or systems using the ks.today API. The API allows you to programmatically access the tables and data you create on ks.today and perform basic CRUD (Create, Read, Update, Delete) operations.

This guide explains the API's logic, authentication steps, endpoint structures, request and response formats with PHP examples.

2. Authentication and Setup

2.1. API Key

All requests to the API must be authenticated with a valid API key.

  • API keys are generated from the ks.today interface on the /api/settings/apikeys/ page.
  • Each generated key is linked to a specific ks.today user.
  • The permissions of the API key are limited by the roles of the associated user. That is, the key can only perform actions that the user is permitted to do.
  • You must send your API key as a Bearer token in the HTTP Authorization header of each request:
Authorization: Bearer <YOUR_API_KEY>

2.2. Base URL

API endpoints operate through a dedicated or central base URL. The base URL to which you will direct your requests is typically in these formats:

  • https://yourcompany.ks.today/api/
  • https://yourwebsite.com/api/
  • (Note: The yourcompany or yourwebsite.com parts will be your own domain name or the ks.today address assigned to you.)

The endpoint examples in this guide are shown as additions to this base URL.

3. Example API Client (PHP)

To facilitate interaction with the API, you can use the following PHP class. This class handles basic tasks such as creating requests, setting headers, sending JSON data, and processing responses.

<?php

/**
 * KsTodayApiClient Class
 * Handles requests to the ks.today API.
 */
class KsTodayApiClient
{
    private static $apiKey = null;
    private static $baseUrl = null;
    private static $defaultTimeout = 30; // seconds

    /**
     * Sets the API Key for subsequent requests.
     *
     * @param string $apiKey The API key.
     */
    public static function setApiKey(string $apiKey)
    {
        self::$apiKey = $apiKey;
    }

    /**
     * Sets the Base URL for the API endpoints.
     * Example: "https://yourcompany.ks.today/api/"
     *
     * @param string $baseUrl The base URL ending with "/api/".
     */
    public static function setBaseUrl(string $baseUrl)
    {
        // Ensure base URL ends with /api/ for consistency
        if (substr($baseUrl, -5) !== '/api/') {
             if (substr($baseUrl, -1) !== '/') {
                 $baseUrl .= '/';
             }
             // If it doesn't end with 'api/', you might want to append it or throw an error.
             // For robust implementation, ensure the URL format is correct.
             // $baseUrl .= 'api/';
        }
        self::$baseUrl = $baseUrl;
    }

    /**
     * Checks if the API Key and Base URL are set.
     * @return bool True if both are set, false otherwise.
     */
    public static function isConfigured(): bool
    {
        return !empty(self::$apiKey) && !empty(self::$baseUrl);
    }

    /**
     * Makes a request to the ks.today API.
     *
     * @param string $endpoint The API endpoint path (e.g., "adm/specs/", "account/user/edit/").
     * @param string $method The HTTP method (GET, POST, PATCH). Note: API uses GET for Delete.
     * @param array $params For GET requests, key-value array for query parameters. For POST/PATCH, the data payload array.
     * @param array $options Additional cURL options if needed (e.g., ['timeout' => 60]).
     * @return array|null The decoded JSON response as an array, or null on failure.
     */
    public static function request(string $endpoint, string $method = 'GET', array $params = [], array $options = []): ?array
    {
        if (!self::isConfigured()) {
            error_log("KsTodayApiClient not configured. Set API Key and Base URL.");
            return null; // Or throw an exception
        }

        $method = strtoupper($method);
        // Ensure URL concatenation is correct
        $url = rtrim(self::$baseUrl, '/') . '/' . ltrim($endpoint, '/');

        $ch = curl_init();

        $curlOptions = [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_SSL_VERIFYHOST => 0, // Use 2 in production environment if possible
            CURLOPT_SSL_VERIFYPEER => 0, // Use true in production environment if possible
            CURLOPT_TIMEOUT => $options['timeout'] ?? self::$defaultTimeout,
            CURLOPT_HTTPHEADER => [
                'Content-Type: application/json',
                'Authorization: Bearer ' . self::$apiKey,
            ],
        ];

        if ($method === 'GET') {
            if (!empty($params)) {
                // Build query string respecting the specific array format for filters
                $queryString = http_build_query($params);
                // Fix array formatting (e.g., filter[field][0]=value -> filter[field][]=value)
                // This replacement is crucial for ks.today filter format
                $queryString = preg_replace('/%5B\d+%5D=/', '[]=', $queryString);
                $url .= '?' . $queryString;
            }
            // Note: GET is also used for DELETE in this API
            if (strpos($endpoint, '/del/') !== false) {
                 // API uses GET for delete, no specific cURL option needed for method
            }
        } elseif ($method === 'POST') {
            $curlOptions[CURLOPT_POST] = true;
            $curlOptions[CURLOPT_POSTFIELDS] = json_encode($params);
        } elseif ($method === 'PATCH') {
            $curlOptions[CURLOPT_CUSTOMREQUEST] = 'PATCH';
            $curlOptions[CURLOPT_POSTFIELDS] = json_encode($params);
        } else {
             // Unsupported method specified by the caller
             error_log("Unsupported HTTP method: " . $method);
             curl_close($ch);
             return null;
        }

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt_array($ch, $curlOptions);

        $result = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

        if (curl_errno($ch)) {
            // Log cURL errors
            error_log("cURL Error (" . $url . "): " . curl_error($ch));
            curl_close($ch);
            return null; // Indicate failure
        }

        curl_close($ch);

        $response = json_decode($result, true);

        // Check if JSON decoding failed
        if ($response === null && json_last_error() !== JSON_ERROR_NONE) {
             // Log JSON decoding errors
             error_log("Failed to decode JSON response from ks.today API (" . $url . "): " . $result);
             return null; // Indicate failure
        }

        // Even if JSON decoded, check the 'result' flag from the API response if present
        if (isset($response['result']) && $response['result'] === false) {
            // Optional: Log the API-level error message if provided
            error_log("ks.today API Error (" . $url . "): " . var_export($response['content'] ?? 'Unknown error', true));
        }

        return $response; // Return the decoded array
    }
}

?>

Usage Example

<?php
// require 'KsTodayApiClient.php'; // Include the class file

// Setup (Usually done once at application startup)
KsTodayApiClient::setApiKey('YOUR_API_KEY');
KsTodayApiClient::setBaseUrl('https://yourcompany.ks.today/api/');

// Make a request (Example: List records)
$endpoint = 'example/table/'; // Replace with your table path
$params = ['list_limit' => 10]; // GET parameters
$response = KsTodayApiClient::request($endpoint, 'GET', $params);

if ($response && ($response['result'] ?? false)) {
    // Request successful and API returned 'result: true'
    $records = $response['content']['list'] ?? [];
    echo "Found " . count($records) . " records.";
    // Process records...
} else {
    // Request failed or API returned 'result: false'
    echo "API request failed.";
    // Log or display error details from $response...
}
?>

4. API Endpoints and CRUD Operations

Below are the methods, endpoints, and required parameters you will use to perform basic CRUD operations on ks.today data.

4.1. Create

Used to create a new record.

  • Method: POST
  • Endpoint: {table_path}/edit/ (e.g., customers/addresses/edit/)
  • Request Body (JSON):
    • postvar: To add a new record, the value of this key must be an empty string ("").
    • Other Fields: Field names and values for the record to be created (e.g., {"v_name": "Value", "i_amount": 10}). Field names must match your definitions in ks.today.
  • Response: Returns {"result": true, ...} on success, {"result": false, ...} on failure. A successful response may include the ID of the created record.

Example (PHP)

<?php
$endpoint = 'products/stock/edit/'; // Your table path + /edit/
$data = [
    'postvar'      => '', // Indicates CREATE operation
    'v_product_name' => 'New Product',
    'i_stock_count' => 100,
    'e_active' => '1'
];
$response = KsTodayApiClient::request($endpoint, 'POST', $data);

if ($response && ($response['result'] ?? false)) {
    echo "Record created successfully. New ID: " . ($response['content']['ID'] ?? 'N/A');
} else {
    echo "Failed to create record.";
    // Log error details from $response if needed
}
?>

4.2. Read (Listing/Retrieving)

Used to list existing records, filter them, or retrieve specific records.

  • Method: GET
  • Endpoint: {table_path}/ (e.g., projects/tasks/)
  • Request Parameters (Query String): These are parameters added to the URL for operations like filtering, pagination, or fetching by ID. You can find details in Section 5.
  • Response: Returns {"result": true, "content": {...}} on success. The content object includes the record list (list), field definitions (fields), pagination information (pagination), and other metadata. Returns {"result": false, ...} on failure.

Example (PHP - Listing and Filtering)

<?php
$endpoint = 'projects/tasks/'; // Your table path
$params = [
    'list_limit' => 25,          // Records per page
    'page' => 2,                // Page number
    'filter' => [                // Example filter: active tasks
        'id_status' => ['1', '2'], // Status ID 1 OR 2 (Assuming 1=Open, 2=In Progress)
        'e_active' => ['1']      // Is active
    ],
    'searchvar' => 'urgent'       // Search for "urgent" in searchable fields
];
$response = KsTodayApiClient::request($endpoint, 'GET', $params);

if ($response && ($response['result'] ?? false)) {
    $records = $response['content']['list'] ?? [];
    echo "Found " . count($records) . " records.";
    // Process records...
} else {
    echo "Failed to list records.";
    // Log error details from $response if needed
}
?>

Example (PHP - Fetching Specific IDs)

<?php
$endpoint = 'projects/tasks/';
$params = ['ids' => '101,105,210']; // Fetch records with these specific IDs
$response = KsTodayApiClient::request($endpoint, 'GET', $params);
// ... response handling ...
?>

Example (PHP - Fetching a Single ID)

<?php
$endpoint = 'projects/tasks/';
// Method 1: Using 'ids' parameter
// $params = ['ids' => '101'];

// Method 2: Using ROW_ID filter (ROW_ID is a standard field representing the record ID)
$params = [
    'filter' => [
        'ROW_ID' => [
            'v_value_start' => '101',
            'v_value_end' => '101'
        ]
    ]
];
$response = KsTodayApiClient::request($endpoint, 'GET', $params);
// ... response handling ...
?>

4.3. Update (Partial)

Used to update specific fields of an existing record.

  • Method: PATCH
  • Endpoint: {table_path}/edit/ (e.g., customers/addresses/edit/)
  • Request Body (JSON):
    • postvar: The ID of the record to be updated.
    • Other Fields: Only the fields to be changed and their new values. The existing values of fields not sent in this request will remain unchanged (preserved).
  • Response: Returns {"result": true} on success, {"result": false, ...} on failure.
Important Note: The PATCH method only updates the fields you send. If another method (e.g., POST) is used instead of PATCH, or if the API behaves differently, fields you don't send might revert to their default values or be reset. It is recommended to use PATCH for partial updates.

Example (PHP)

<?php
$recordIdToUpdate = 55; // ID of the record to update
$endpoint = 'customers/addresses/edit/'; // Your table path + /edit/
$data = [
    'postvar'       => $recordIdToUpdate, // Indicates UPDATE for this ID
    'v_address_line1' => 'New Address Information', // Update only this field
    'v_postal_code'   => '34000'              // Update only this field
    // Other fields like 'v_city' are not sent, their values will remain unchanged
];
$response = KsTodayApiClient::request($endpoint, 'PATCH', $data);

if ($response && ($response['result'] ?? false)) {
    echo "Record ID " . $recordIdToUpdate . " updated successfully.";
} else {
    echo "Failed to update record.";
    // Log error details from $response if needed
}
?>

4.4. Delete

Used to delete one or more records.

Caution: The ks.today API uses the GET method for the delete operation instead of the standard DELETE method. The distinguishing feature is the URL structure.
  • Method: GET
  • Endpoint: {table_path}/del/{IDs}
    • {IDs}: The ID of the record(s) to be deleted, or a comma-separated list of IDs.
    • Example (Single): products/stock/del/15
    • Example (Multiple): products/stock/del/15,18,21
  • Request Body: Not required (empty).
  • Response: Returns {"result": true} on success, {"result": false, ...} on failure.

Example (PHP - Single Delete)

<?php
$recordIdToDelete = 15;
$endpoint = 'products/stock/del/' . $recordIdToDelete; // Append ID directly

// No body/params needed for DELETE via GET request to this endpoint structure
$response = KsTodayApiClient::request($endpoint, 'GET');

if ($response && ($response['result'] ?? false)) {
    echo "Record ID " . $recordIdToDelete . " deleted successfully.";
} else {
    echo "Failed to delete record.";
    // Log error details from $response if needed
}
?>

Example (PHP - Multiple Delete)

<?php
$recordIdsToDelete = '15,18,21'; // Comma-separated IDs
$endpoint = 'products/stock/del/' . $recordIdsToDelete; // Append IDs directly

$response = KsTodayApiClient::request($endpoint, 'GET');

if ($response && ($response['result'] ?? false)) {
    echo "Record IDs " . $recordIdsToDelete . " deleted successfully.";
} else {
    echo "Failed to delete records.";
    // Log error details from $response if needed
}
?>

5. Read Operation Details (Filtering, Pagination, Response Structure)

The GET /{table_path}/ endpoint offers powerful filtering and pagination capabilities for reading data.

5.1. Pagination

  • list_limit=N: Specifies how many records to show per page (e.g., list_limit=100). The default is usually 20.
  • page=N: Specifies which page you want to retrieve (starts from 1, e.g., page=3). The default is 1.

5.2. Record Filtering

You can use various filter parameters to find the records you want. These parameters are added to the URL query string.

  • General Search (searchvar):
    • searchvar=term: Performs a text-based search (usually with LIKE) in fields marked as searchable.
    • Example: ?searchvar=report
  • Fetch by ID (ids):
    • ids=ID1,ID2,...: Retrieves only the records with the specified IDs.
    • Example: ?ids=5,12,25
  • Field-Based Filters (filter, filterIsNull, filterNotIn):
    Note: When using http_build_query in PHP, it's important to preserve the filter[fieldName][]=value format (with square brackets). The example client class handles this automatically.
    • Equality / Contains (filter[FieldName][]=Value):
      • Related Field (ID): filter[user_id][]=10 (Records where User ID is 10)
      • Text Field (Contains): filter[description][]=important (Records where description contains "important")
      • Select Field: filter[status][]=active (Records where status is "active")
      • Multiple Values (OR): filter[status][]=active&filter[status][]=pending (Records where status is "active" OR "pending")
    • NULL Check (filterIsNull[FieldName][]=Status):
      • Not Null (IS NOT NULL): filterIsNull[end_date][]=0
      • Is Null (IS NULL): filterIsNull[end_date][]=1
    • Exclusion (filterNotIn[FieldName][]=Value):
      • filterNotIn[category][]=old (Records where category is not "old")
      • Multiple Values: filterNotIn[user_id][]=1&filterNotIn[user_id][]=2 (Records where User ID is not 1 OR 2)
    • Range - Numeric (filter[FieldName][v_value_start/end]=Value):
      • filter[price][v_value_start]=100 (Price >= 100)
      • filter[price][v_value_end]=500 (Price <= 500)
      • Both together: between 100 and 500 (inclusive).
    • Range - Date/Datetime (filter[FieldName][v_time_start/end]=Value):
      • filter[created_date][v_time_start]=2024-01-01 (>= Jan 1, 2024 00:00:00)
      • filter[created_date][v_time_end]=2024-12-31 (<= Dec 31, 2024 23:59:59)
      • For datetime fields, the YYYY-MM-DD HH:MM:SS format can also be used. If only a date is sent, 00:00:00 is added for the start and 23:59:59 for the end.
    • Predefined Date Range (filter[FieldName][v_tp]=ID):
      • filter[created_date][v_tp]=7 (Records from this month)
      • Not used together with v_time_start or v_time_end.
      • v_tp IDs and Meanings:
        • 1: Today
        • 2: Yesterday
        • 3: Last 7 Days
        • 4: Last Week
        • 5: This Week
        • 6: Last 30 Days
        • 7: This Month
        • 8: Last Month
        • 9: This Year
        • 10: Next 30 Days
        • 11: Before Today (<= Today)
        • 12: After Today (>= Today)
        • 13: Last Year

5.3. Response Structure

Successful responses to GET requests ({"result": true, ...}) contain rich metadata under the content key, describing both the data and its structure. This allows you to build dynamic interfaces.

  • content.active: Basic information about the table/model you accessed.
  • content.tableName: The actual table name in the database.
  • content.list: An array containing the filtered and paginated records.
  • content.fields: Detailed definition of each field in the table. This section includes the field name, display name (v_name), type (text, number, date, select, parent, etc.), whether it's related, its options, display order, and more. You can use this information to dynamically generate forms and tables.
  • content.pagination: Pagination information (current page, total number of pages, etc.).
  • content.parents: If the table contains fields linked to other tables (parent type), this includes additional information for these fields. The values array here provides a list of related values (ID and text) corresponding to the records currently present in the list (content.list).
    Fetching All Related Values: If you need to retrieve all possible values for a related field (e.g., Categories) to populate filter options, the content.parents...values list will not be sufficient. Instead, you need to find the API path of the parent table from the field's metadata (content.fields...parent.CntName) and make a separate GET request to that path (e.g., GET /projects/categories/?list_limit=1000). You can then populate your filter interface (e.g., dropdown) with the complete list obtained this way. To search by name within a related field, you can also send a request with searchvar to the parent table's endpoint.
  • content.FilterableFields, content.filterIsNull, filterNotIn: Structures indicating which fields can be filtered and how, also reflecting the active filters sent in your request.
  • content.FilterAjaxValues: Shows the ID and text correspondences for related fields belonging to the active filters.
  • content.ListSub: Contains definitions of sub-tables related to the main table, if any.
  • content.modelConfig: General settings of the table in ks.today.

6. Retrieving Metadata (Field List)

If you want to learn the field structure of a table (e.g., to dynamically create a filtering form or data entry form), you can make a normal listing request even if the table is empty. Using a low limit (e.g., list_limit=1) is sufficient. The content.fields section in the response will provide you with the field definitions.

<?php
$endpoint = 'products/categories/'; // Your table path
$params = ['list_limit' => 1]; // Get metadata even if no records exist
$response = KsTodayApiClient::request($endpoint, 'GET', $params);

if ($response && ($response['result'] ?? false)) {
    $fields = $response['content']['fields'] ?? [];
    $filterableFieldsInfo = $response['content']['FilterableFields'] ?? [];
    // Use $fields and $filterableFieldsInfo to dynamically build UI elements
    echo "Field definitions retrieved successfully.";
    // print_r($fields);
} else {
    echo "Failed to retrieve field information.";
    // Log error details from $response if needed
}
?>

7. Summary Table

Operation Method Endpoint Suffix ID Location Body Required? Core Body Parameters
Create POST /edit/ N/A Yes postvar: "", Fields
Read (Listing) GET / Query Parameters No N/A
Read (Single/Multiple ID) GET / Query (ids or filter[ROW_ID]) No N/A
Update (Partial) PATCH /edit/ Body (postvar) Yes postvar: RECORD_ID, Fields
Delete GET /del/{ID1,ID2...} URL Path No N/A

8. Conclusion

The ks.today API offers a comprehensive way to programmatically access and manage your data. With the methods and endpoints described in this guide, you can perform basic CRUD operations, leverage powerful filtering capabilities, and build dynamic applications thanks to the rich metadata provided by the API.

Additional support or documentation may be required for specific situations or more advanced usage scenarios encountered during integration.

This website uses cookies and similar technologies to ensure that we give you the best possible service. By clicking on “Accept” you are agreeing to the processing of your data as well as its transfer to third party providers. The data will be used for analyses, re-targeting and to provide personalized content on websites by third party providers. For more information, including regarding the processing of data by third party providers, see your settings and our Privacy Policy. You can decline the use of cookies or change your settings at any time. To the Corporate Info.
More info