Irongist API Documentation

The Irongist API allows you to interact with your encrypted database tables using simple HTTP requests. All values are automatically encrypted, indexed (when possible), and decrypted internally. This documentation covers everything you need to build secure applications quickly.

💡 Quick Start: After creating a table in your dashboard, copy your API key and table ID. Then make your first request using cURL, JavaScript, Python, or any HTTP client.

Base URL & Authentication

POST /api/{keyId}/{action}

Actions: insert, select, update, delete

Example: POST /api/abc123def456/select

Authentication

X-API-KEY: your_api_key_here
⚠ Required for all requests. Requests without a valid API key will be rejected with a 401 error and response: {"message": "No API key provided", "status": "error"}.

Your API key is unique to your account. Keep it secure. You can rotate keys from your dashboard at any time.

List All Tables (GET /api/tables)

Retrieve a list of all tables in your account, including their schema, record count, and column definitions. This endpoint is useful for discovering available tables and their structure programmatically.

Request

GET /api/tables
X-API-KEY: your_api_key

No request body is required.

Response Format

The response contains a tables array. Each object represents one table with the following properties:

  • tableName – Name of the table (string)
  • records – Total number of records currently in the table (integer)
  • keyId – Unique identifier for the table (used in API endpoints like /api/{keyId}/select)
  • columns – Array of column objects, each with columnName and columnType

Example Response (Fake Data)

{
    "tables": [
        {
            "tableName": "table1",
            "records": 42,
            "keyId": "a1b2c3d4e5f67890",
            "columns": [
                { "columnName": "id", "columnType": "auto" },
                { "columnName": "createdAt", "columnType": "timestamp" },
                { "columnName": "updatedAt", "columnType": "timestamp" },
                { "columnName": "name", "columnType": "text" },
                { "columnName": "email", "columnType": "email" },
                { "columnName": "age", "columnType": "number" }
            ]
        },
        {
            "tableName": "table2",
            "records": 12,
            "keyId": "f9e8d7c6b5a43210",
            "columns": [
                { "columnName": "id", "columnType": "auto" },
                { "columnName": "createdAt", "columnType": "timestamp" },
                { "columnName": "updatedAt", "columnType": "timestamp" },
                { "columnName": "product_name", "columnType": "text" },
                { "columnName": "price", "columnType": "number" },
                { "columnName": "in_stock", "columnType": "number" }
            ]
        },
        {
            "tableName": "table3",
            "records": 7,
            "keyId": "1122334455667788",
            "columns": [
                { "columnName": "id", "columnType": "auto" },
                { "columnName": "createdAt", "columnType": "timestamp" },
                { "columnName": "updatedAt", "columnType": "timestamp" },
                { "columnName": "title", "columnType": "text" },
                { "columnName": "due_date", "columnType": "date" },
                { "columnName": "completed", "columnType": "number" }
            ]
        }
    ]
}
💡 Tip: Use this endpoint to fetch the keyId for a table dynamically. Then use that keyId in subsequent /api/{keyId}/select (or insert/update/delete) requests. The columns list includes system fields (id, createdAt, updatedAt) plus all custom columns you defined.

cURL Example

curl -X GET https://api.irongist.com/api/tables \
  -H "X-API-KEY: your_api_key"

Error Responses

Same as other endpoints: missing/invalid API key returns 401; server errors return 500.

Column Types

When creating a table, you define columns with specific types. Each type has automatic validation and normalization before encryption.

TypeStorage FormatNormalizationExample Input
textEncrypted stringLowercase, trimmed"Hello World" → "hello world"
numberEncrypted stringNormalized numeric (removes trailing zeros)"123.00" → "123"
dateEncrypted stringY-m-d format"2024-12-25"
timestampEncrypted integer (Unix timestamp)Stored as plain integer (seconds since epoch)132732583
emailEncrypted stringLowercase, trimmed, format validation"User@Example.com" → "user@example.com"
⚠ Important: All values are normalized before encryption. For timestamp, you must provide a valid Unix timestamp (integer). Invalid or non‑numeric values will be rejected. Comparisons like gt, lt, between work directly on the numeric timestamp value.

System Fields

Every table has these automatic system fields — you don't need to define them:

  • id – Auto‑incrementing primary key (number)
  • createdAt – Unix timestamp (seconds since epoch) when row was created
  • updatedAt – Unix timestamp (seconds since epoch) when row was last updated
💡 Note: System fields are automatically indexed and can be used in WHERE clauses and ORDER BY. These are the only fields that support ordering. Both createdAt and updatedAt are Unix timestamps — the same format used by custom timestamp columns.

Insert

Insert a new record into the table. All custom fields defined in your table schema must be provided.

Request Example

POST /api/{keyId}/insert
X-API-KEY: your_api_key

{
    "first_name": "John",
    "last_name": "Doe",
    "email": "john@example.com",
    "age": 30,
    "last_login": 1678901234
}

Response Example

{
    "action": "insert",
    "record": {
        "id": 1,
        "createdAt": 1775946157,
        "updatedAt": 1775946157,
        "first_name": "John",
        "last_name": "Doe",
        "email": "john@example.com",
        "age": 30,
        "last_login": 1678901234
    }
}
⚠ Required: All custom fields defined in the table must be present in the request. Missing fields will return an error.

Select (Query)

Query records from the table with powerful filtering, pagination, and sorting options.

Request Parameters

ParameterTypeRequiredDefaultDescription
selectarray✅ Yes-Fields to return (system + custom fields)
whereobject❌ No{}Filter conditions
orderByobject❌ Noid ASCSorting (system fields only: id, createdAt, updatedAt)
limitinteger❌ No50Max records to return (max 100)
offsetinteger❌ No0Number of records to skip for pagination
cacheboolean | object❌ NofalseOptional select-only response cache. Use true for default 60s TTL or { "enabled": true, "ttl": 120 } to control TTL (max 300s).
💡 Validation: select must be a non-empty array of valid field names. where and orderBy must be objects. limit and offset must be non-negative integers. Sort direction must be ASC or DESC. cache must be either a boolean or an object with optional enabled and ttl.

Basic Examples

Get all records (first 50)

{
    "select": ["id", "first_name", "email", "createdAt"]
}

With pagination

{
    "select": ["first_name", "email"],
    "limit": 20,
    "offset": 0
}

With opt-in caching

{
    "select": ["first_name", "email"],
    "where": {
        "email": { "eq": "john@example.com" }
    },
    "cache": {
        "enabled": true,
        "ttl": 120
    }
}

Exact match (uses index – fast)

{
    "select": ["first_name", "email"],
    "where": {
        "email": { "eq": "john@example.com" }
    }
}

Multiple conditions (implicit AND)

{
    "select": ["first_name", "email", "age"],
    "where": {
        "email": { "eq": "john@example.com" },
        "age": { "gt": 18 }
    }
}

IN operator (uses index – fast)

{
    "select": ["first_name", "email"],
    "where": {
        "id": { "in": [1, 2, 3, 4, 5] }
    }
}

BETWEEN operator (range)

{
    "select": ["first_name", "age"],
    "where": {
        "age": { "between": [18, 65] }
    }
}

LIKE operator (pattern matching)

{
    "select": ["first_name", "email"],
    "where": {
        "first_name": { "like": "%john%" }
    }
}

Order By Examples

⚠ Note: ORDER BY only works on system fields: id, createdAt, updatedAt. Custom fields cannot be ordered due to encryption.
{
    "select": ["first_name", "email"],
    "orderBy": { "createdAt": "DESC" },
    "limit": 10
}

Complex WHERE Examples

OR condition

{
    "select": ["first_name", "email"],
    "where": {
        "OR": [
            { "email": { "eq": "john@example.com" } },
            { "email": { "eq": "jane@example.com" } }
        ]
    }
}

Nested AND/OR

{
    "select": ["first_name", "email", "age"],
    "where": {
        "AND": [
            {
                "OR": [
                    { "first_name": { "like": "%john%" } },
                    { "first_name": { "like": "%jane%" } }
                ]
            },
            { "age": { "gte": 18 } },
            { "status": { "eq": "active" } }
        ]
    },
    "orderBy": { "createdAt": "DESC" },
    "limit": 50,
    "offset": 0
}

Response Format

{
    "data": [ ... ],
    "limit": 50,
    "offset": 0
}
⚡ Performance: eq and in queries use indexes for fast lookups. Other operators (like, gt, lt, between, neq) fall back to full scan — use indexed operators when possible.
💡 Cache behavior: Cached select results are automatically invalidated whenever the same table receives an insert, update, or delete request. That keeps reads fast without serving stale data after writes.

Update

⚠ Safety Feature: UPDATE requests MUST include a where clause. This prevents accidental updates of all records.
💡 Validation: The update object may only contain custom table columns. System fields like id, createdAt, and updatedAt are read-only.

Request Example

POST /api/{keyId}/update
X-API-KEY: your_api_key

{
    "update": {
        "first_name": "Jonathan",
        "age": 31,
        "last_login": 1679999999
    },
    "where": {
        "id": { "eq": 1 }
    }
}

Response Example

{
    "action": "update",
    "affected": 1
}
💡 Note: The response shows the number of affected rows. Updated rows are automatically re‑encrypted and re‑indexed. System field updatedAt is automatically updated to the current Unix timestamp.

Delete

⚠ Safety Feature: DELETE requests MUST include a non‑empty where clause. This prevents accidental deletion of all records.

Request Example

POST /api/{keyId}/delete
X-API-KEY: your_api_key

{
    "where": {
        "id": { "eq": 1 }
    }
}

Response Example

{
    "action": "delete",
    "deleted": 1
}

Invalid Requests (Rejected)

{
    // Missing "where" field – REJECTED
}
{
    "where": {}  // Empty object – REJECTED
}

WHERE Operators Reference

OperatorDescriptionIndexed?Example
eqEquals✅ Yes{"age": {"eq": 25}}
neqNot equals❌ No{"status": {"neq": "deleted"}}
gtGreater than❌ No{"age": {"gt": 18}}
gteGreater than or equal❌ No{"age": {"gte": 18}}
ltLess than❌ No{"age": {"lt": 65}}
lteLess than or equal❌ No{"age": {"lte": 65}}
likePattern matching (% wildcard)❌ No{"name": {"like": "%john%"}}
inIn array✅ Yes{"id": {"in": [1,2,3]}}
betweenRange (inclusive)❌ No{"age": {"between": [18,65]}}
💡 Tip: Use indexed operators (eq, in) whenever possible for best performance. These are typically 10-100x faster than non-indexed operators.

Encryption & Security Model

Irongist takes data security seriously. Here's how encryption works:

  • Encryption at rest: All data stored in our database is encrypted using industry-standard algorithms (AES-256).
  • Per-request decryption: When you query data, it's decrypted on the fly and returned as plaintext over HTTPS.
  • Indexed fields: For eq and in operators, we create encrypted indexes that allow fast lookups without decrypting every record.
  • Normalization before encryption: Values are normalized (lowercase, trimmed, formatted) to ensure consistent matching while maintaining security.
  • HTTPS only: All API endpoints require HTTPS. HTTP requests are rejected.
  • Timestamp handling: Unix timestamps are stored as encrypted integers, preserving numeric ordering for range queries.
🔒 Security first: Your encryption keys are managed securely and never exposed.
NEVER EXPOSE YOUR API KEY, IF YOU ARE USING IT IN FRONTEND MAKE SURE TO PROXY THE REQUEST SO YOUR KEY IS NEVER EXPOSED

Performance Guide

  • Fast (indexed): eq, in — typically under 50ms
  • Slow (full scan): like, gt, gte, lt, lte, between, neq — can be 200ms+ on large tables

Best Practices:

  • Always use limit — never query without a reasonable limit
  • Prefer eq and in operators for frequently queried fields
  • Keep table sizes reasonable (under 100,000 rows for optimal performance)
  • Avoid deep nesting of AND/OR conditions (max depth 10)
  • Use pagination (limit + offset) for large result sets
  • For timestamp columns, use numeric comparisons (gt, lt, between) — they are supported but will trigger a full scan unless indexed (future enhancement).

Error Responses

All error responses follow this format:

{
    "message": "Error description",
    "status": "error"
}

HTTP status codes are also set appropriately (400, 401, 404, 500).

Validation failures return 400 responses. Unexpected server-side failures return a generic 500 response and do not expose internal exception details.

Common Error Responses

{
    "message": "Invalid field in select: unknown_field",
    "status": "error"
}
{
    "message": "Invalid field in where: unknown_field",
    "status": "error"
}
{
    "message": "Invalid sort direction for createdAt. Use ASC or DESC",
    "status": "error"
}
{
    "message": "For insert action please make sure all columns are present, missing: first_name",
    "status": "error"
}
{
    "message": "Invalid email value for email",
    "status": "error"
}
{
    "message": "Invalid timestamp value, must be a positive integer",
    "status": "error"
}
{
    "message": "No API key provided",
    "status": "error"
}
{
    "message": "Update requires a non-empty where clause",
    "status": "error"
}

Complete Working Examples

Example 1: User Registration Flow

// 1. Insert new user with timestamp
POST /api/{usersKeyId}/insert
{
    "username": "john_doe",
    "email": "john@example.com",
    "password_hash": "...",
    "registered_at": 1678901234
}

// 2. Query the user
POST /api/{usersKeyId}/select
{
    "select": ["id", "username", "email", "createdAt", "registered_at"],
    "where": {
        "email": { "eq": "john@example.com" }
    }
}

// 3. Update user (e.g., last login timestamp)
POST /api/{usersKeyId}/update
{
    "update": { "last_login": 1680000000 },
    "where": { "id": { "eq": 1 } }
}

// 4. Delete user
POST /api/{usersKeyId}/delete
{
    "where": { "id": { "eq": 1 } }
}

Example 2: Paginated Post List

// Get approved posts, page 1 (first 20)
{
    "select": ["id", "title", "content", "createdAt"],
    "where": { "status": { "eq": "approved" } },
    "orderBy": { "createdAt": "DESC" },
    "limit": 20,
    "offset": 0
}

Example 3: Search Functionality

{
    "select": ["id", "title", "content"],
    "where": {
        "OR": [
            { "title": { "like": "%news%" } },
            { "content": { "like": "%news%" } }
        ]
    },
    "limit": 50
}

Example 4: Advanced Filtering with Range (using timestamp)

{
    "select": ["id", "title", "likes", "published_at"],
    "where": {
        "AND": [
            { "status": { "eq": "published" } },
            { "likes": { "between": [10, 100] } },
            { "published_at": { "gt": 1678901234 } }
        ]
    },
    "orderBy": { "createdAt": "DESC" },
    "limit": 30
}

Example 5: cURL for Quick Testing

curl -X POST https://api.irongist.com/api/your-key-id/select \
  -H "X-API-KEY: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "select": ["id", "name", "email"],
    "where": { "status": { "eq": "active" } },
    "limit": 10
  }'

Example 6: Fetch All Tables (GET /api/tables)

curl -X GET https://api.irongist.com/api/tables \
  -H "X-API-KEY: your_api_key"

Limits & Constraints

ConstraintValueDescription
Max limit100Maximum records per request
Default limit50Default if not specified
Max nesting depth10Maximum AND/OR nesting levels
ORDER BY fieldsid, createdAt, updatedAtOnly system fields support ordering
Min text length1Text fields cannot be empty
Max tables (free)20Per account, can be increased
Max columns per table20Per table, can be increased on request
📘 Need help? contact support@irongist.com for priority assistance.