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.
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
{"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
columnNameandcolumnType
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" }
]
}
]
}
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.
| Type | Storage Format | Normalization | Example Input |
|---|---|---|---|
| text | Encrypted string | Lowercase, trimmed | "Hello World" → "hello world" |
| number | Encrypted string | Normalized numeric (removes trailing zeros) | "123.00" → "123" |
| date | Encrypted string | Y-m-d format | "2024-12-25" |
| timestamp | Encrypted integer (Unix timestamp) | Stored as plain integer (seconds since epoch) | 132732583 |
| Encrypted string | Lowercase, trimmed, format validation | "User@Example.com" → "user@example.com" |
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
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
}
}
Select (Query)
Query records from the table with powerful filtering, pagination, and sorting options.
Request Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
select | array | ✅ Yes | - | Fields to return (system + custom fields) |
where | object | ❌ No | {} | Filter conditions |
orderBy | object | ❌ No | id ASC | Sorting (system fields only: id, createdAt, updatedAt) |
limit | integer | ❌ No | 50 | Max records to return (max 100) |
offset | integer | ❌ No | 0 | Number of records to skip for pagination |
cache | boolean | object | ❌ No | false | Optional select-only response cache. Use true for default 60s TTL or { "enabled": true, "ttl": 120 } to control TTL (max 300s). |
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
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
}
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.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
where clause. This prevents accidental updates of all records.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
}
updatedAt is automatically updated to the current Unix timestamp.Delete
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
| Operator | Description | Indexed? | Example |
|---|---|---|---|
eq | Equals | ✅ Yes | {"age": {"eq": 25}} |
neq | Not equals | ❌ No | {"status": {"neq": "deleted"}} |
gt | Greater than | ❌ No | {"age": {"gt": 18}} |
gte | Greater than or equal | ❌ No | {"age": {"gte": 18}} |
lt | Less than | ❌ No | {"age": {"lt": 65}} |
lte | Less than or equal | ❌ No | {"age": {"lte": 65}} |
like | Pattern matching (% wildcard) | ❌ No | {"name": {"like": "%john%"}} |
in | In array | ✅ Yes | {"id": {"in": [1,2,3]}} |
between | Range (inclusive) | ❌ No | {"age": {"between": [18,65]}} |
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
eqandinoperators, 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.
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
eqandinoperators 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).
Pagination Guide
Use limit and offset to paginate through results.
- Page 1:
{"limit": 20, "offset": 0} - Page 2:
{"limit": 20, "offset": 20} - Page N:
{"limit": L, "offset": (N-1) * L}
limit is 100. Requests with limit > 100 will be capped to 100 automatically.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
| Constraint | Value | Description |
|---|---|---|
| Max limit | 100 | Maximum records per request |
| Default limit | 50 | Default if not specified |
| Max nesting depth | 10 | Maximum AND/OR nesting levels |
| ORDER BY fields | id, createdAt, updatedAt | Only system fields support ordering |
| Min text length | 1 | Text fields cannot be empty |
| Max tables (free) | 20 | Per account, can be increased |
| Max columns per table | 20 | Per table, can be increased on request |