SQLite: SQL Operations
Section titled “SQLite: SQL Operations”Run SQL against your SQLite database, create new database files, execute transactional batches with ACID guarantees, and run maintenance operations such as WAL checkpoints, VACUUM INTO, and integrity checks. Use these endpoints when you need to mutate schema or data, share a query via a URL, or perform offline-friendly compaction on a database file inside your container.
All endpoints in this group target the container’s SQLite service at https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com.
List databases in a directory
Section titled “List databases in a directory”GET /api/v1/sqlite/db/list
Section titled “GET /api/v1/sqlite/db/list”List the files in one directory whose extension is .db, .sqlite, or .sqlite3, newest first. Subdirectories, symlinks, and other non-regular entries are skipped. is_valid reports whether the file starts with a SQLite header. Without the --allow-any-absolute-db-path flag, only /hoody/databases and directories under it can be listed. The databases field is null when nothing matches.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
dir | query | string | No | Absolute path of the directory to list. Default: "/hoody/databases" |
timeout | query | integer | No | Deadline for this request, in seconds, clamped to [1, 300]. It sets the request context deadline that handlers check between steps, so a long operation stops at the next checkpoint rather than being cut off mid-step; work already in progress, such as a filesystem scan or a wait for another writer, runs to completion. This is a server-side deadline, not a client transport timeout. Omitted, the server default applies (30 seconds unless the deployment overrides it). Default: 30 |
Response
Section titled “Response”{ "success": true, "count": 2, "dir": "/hoody/databases", "databases": [ { "name": "my-database.db", "path": "/hoody/databases/my-database.db", "size": 24576, "size_str": "24.0 KB", "modified": "2026-01-15T10:30:00Z", "mod_unix": 1736937000, "is_valid": true }, { "name": "archive.sqlite3", "path": "/hoody/databases/archive.sqlite3", "size": 1048576, "size_str": "1.0 MB", "modified": "2026-01-12T08:15:00Z", "mod_unix": 1736667300, "is_valid": true } ]}{ "code": "INVALID_PARAMETERS", "error": "dir is not an absolute path or is not an existing directory"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INVALID_PARAMETERS | Invalid request parameters | A parameter or the request body is missing, malformed or out of range. This is the default code for a 400 that has no more specific one. | Check parameter types and values against the API specification; the error text names the problem |
{ "code": "FORBIDDEN", "error": "dir resolves outside /hoody/databases through a symlink"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
FORBIDDEN | Forbidden | The request is not allowed: a path that escapes the database directory, or a request refused by the Source IP Guard (Hoody Kit programs are reached through their URLs only) | Use a path inside the permitted directory, and reach the program through its URL |
{ "code": "INTERNAL_ERROR", "error": "An internal server error occurred"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INTERNAL_ERROR | Internal server error | An unexpected server-side failure. Default code for a 500 that has no more specific one; details are in the server log | Retry later; report it if it persists |
| Error Code | Title | Description | Resolution |
|---|---|---|---|
SERVICE_UNAVAILABLE | Service unavailable | The service could not complete the request right now. Default code for a 503 that has no more specific one | Retry after a short delay |
REQUEST_TIMEOUT | Request deadline exceeded | The request did not finish before its deadline (the timeout parameter, default 30 s); nothing was committed | Retry, or raise timeout (max 300) |
SDK usage
Section titled “SDK usage”curl -G "https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com/api/v1/sqlite/db/list" \ --data-urlencode "dir=/hoody/databases"import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
const result = await client.sqlite.databases.list({ dir: '/hoody/databases',});Execute shareable SQL query
Section titled “Execute shareable SQL query”GET /api/v1/sqlite/query
Section titled “GET /api/v1/sqlite/query”Run one read-only query passed in the URL, so the link can be shared: a single SELECT or WITH statement, without a semicolon. Anything else is refused with 400; use POST /api/v1/sqlite/db for it. SQL holding a NUL byte is refused with 400 too (code SQL_ERROR): SQLite would stop reading at the NUL and ignore the rest. The sql value is base64url-encoded (with or without = padding); plain SQL text is also accepted.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
db | query | string | Yes | Database file path |
sql | query | string | Yes | The SQL query, base64url-encoded (with or without = padding). Plain SQL text is also accepted. |
timeout | query | integer | No | Deadline for this request, in whole seconds, clamped to [1, 300]. Once it passes, a long operation stops at its next checkpoint rather than being cut off mid-step; a step already running, such as a filesystem scan or a wait for another writer, finishes first. A request that has not finished by then answers 503 REQUEST_TIMEOUT, except that a write which has already committed still returns its success. A value that is not a whole number is ignored and the default applies. This is a server-side deadline, not a client transport timeout. Omitted, the server default applies (30 seconds unless the deployment overrides it). Default: 30 |
Response
Section titled “Response”{ "columns": ["id", "name", "email"], "query": "SELECT id, name, email FROM users", "resultSet": [ { "id": 1, "name": "Ada Lovelace", "email": "ada@example.com" }, { "id": 2, "name": "Alan Turing", "email": "alan@example.com" } ], "rowCount": 2, "success": true, "truncated": false}{ "code": "INVALID_DB_PATH", "error": "The provided database path is invalid or inaccessible"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INVALID_PARAMETERS | Invalid request parameters | A parameter or the request body is missing, malformed or out of range. This is the default code for a 400 that has no more specific one. | Check parameter types and values against the API specification; the error text names the problem |
INVALID_DB_PATH | Invalid database path | The db/path value is empty, relative, outside /hoody/databases (unless the server allows any absolute path), contains ? or #, escapes via a symlink, or names something that is not a regular file | Provide an absolute path under /hoody/databases, or a bare name / ./name shorthand (resolved to /hoody/databases/*.db) |
DATABASE_NOT_FOUND | Database file does not exist | The database file does not exist and this request does not create it | Check the path, create the database first, or pass create_db_if_missing=true where the operation supports it |
INVALID_SQLITE_HEADER | Not a valid SQLite database | The file exists but is empty or does not start with the SQLite header | Point db at a SQLite database file |
PATH_IS_DIRECTORY | Path is a directory | Expected a database file but the path is a directory. Only reading, writing, deleting and checking a single key and listing keys (without at_timestamp) accept a directory, given as an absolute path (directory mode) | Use a .db file path |
SQL_ERROR | SQL statement failed | SQLite rejected the statement: syntax error, unknown table or column, datatype mismatch, value too big. The error text is SQLite’s own message | Fix the SQL; retrying the same statement fails the same way |
SQL_BIND_ERROR | Bind parameters do not match the statement | Wrong number of positional values, a missing named value, a named value whose name does not start with a letter, or a value that cannot be bound | Send one scalar per placeholder: an array for ?, ?NNN and $NNN, an object for :name, @name and $name (each name starting with a letter) |
{ "code": "RESPONSE_TOO_LARGE", "error": "The result would exceed the response size limit"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
PAYLOAD_TOO_LARGE | Request too large | The request exceeds a size or item-count limit | Split the request |
RESPONSE_TOO_LARGE | Result too large | The result would exceed the response size limit; nothing was committed | Narrow the query or page through it with LIMIT/OFFSET |
{ "code": "DATABASE_ERROR", "error": "An internal database error occurred"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INTERNAL_ERROR | Internal server error | An unexpected server-side failure. Default code for a 500 that has no more specific one; details are in the server log | Retry later; report it if it persists |
DATABASE_ERROR | Database fault | SQLite failed for a reason that is not the request: I/O error, corruption, disk full, a file that cannot be opened. The message is sanitised; details are in the server log | Check the server log, disk space and the database file; the database may need repair |
{ "code": "DATABASE_BUSY", "error": "The database was locked by another writer"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
SERVICE_UNAVAILABLE | Service unavailable | The service could not complete the request right now. Default code for a 503 that has no more specific one | Retry after a short delay |
REQUEST_TIMEOUT | Request deadline exceeded | The request did not finish before its deadline (the timeout parameter, default 30 s); nothing was committed | Retry, or raise timeout (max 300) |
DATABASE_BUSY | Database busy | The database was locked by another writer or in use for longer than the request could wait; nothing was changed | Retry after a short delay |
SDK usage
Section titled “SDK usage”curl -G "https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com/api/v1/sqlite/query" \ --data-urlencode "db=my-database.db" \ --data-urlencode "sql=U0VMRUNUICogRlJPTSB1c2Vycw=="import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
const result = await client.sqlite.sql.queryReadOnly({ db: 'my-database.db', sql: 'U0VMRUNUICogRlJPTSB1c2Vycw==',});Execute SQL transaction
Section titled “Execute SQL transaction”POST /api/v1/sqlite/db
Section titled “POST /api/v1/sqlite/db”Execute multiple SQL queries or statements in a single transaction with full ACID guarantees. Each item sends its SQL as query or statement (alias sql) and its bind values as values (alias params). A statement whose SQL produces result columns (SELECT, WITH, PRAGMA, INSERT/UPDATE/DELETE ... RETURNING) returns its rows exactly like a query; one that produces none returns rowsUpdated. An unknown key on an item is refused with 400 UNKNOWN_FIELD, and values and params with different contents with 400 VALUES_CONFLICT.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
db | query | string | Yes | Database path (absolute path, bare name, or ./name shorthand resolved to /hoody/databases/*.db) |
create_db_if_missing | query | boolean | No | Create database file if it is missing. The legacy alias auto_create is still accepted and means the same thing; the two must agree when both are sent. Default: false |
timeout | query | integer | No | Deadline for this request, in whole seconds, clamped to [1, 300]. Once it passes, a long operation stops at its next checkpoint rather than being cut off mid-step; a step already running, such as a filesystem scan or a wait for another writer, finishes first. A request that has not finished by then answers 503 REQUEST_TIMEOUT, except that a write which has already committed still returns its success. A value that is not a whole number is ignored and the default applies. This is a server-side deadline, not a client transport timeout. Omitted, the server default applies (30 seconds unless the deployment overrides it). Default: 30 |
Request body
Section titled “Request body”| Field | Type | Required | Description |
|---|---|---|---|
resultFormat | string | No | Result format hint (e.g. json) |
transaction | array | No | Ordered list of statements to execute inside the transaction |
Each entry in transaction accepts the following fields:
| Field | Type | Required | Description |
|---|---|---|---|
query | string | No | SQL that returns rows (SELECT, WITH, PRAGMA, ... RETURNING). The item answers resultHeaders and resultSet (or resultSetList). |
statement | string | No | SQL statement to execute (preferred field) |
sql | string | No | Alias for statement (backward compatibility) |
values | array or object | No | Bind parameters: a JSON array for positional (?) parameters, or a JSON object for named (:name) parameters. Elements must be JSON scalars (string, number, boolean, null); numbers must fit int64/float64 (else 400). A nested array or object cannot be bound: the request fails with 400 SQL_BIND_ERROR, or, for an item with noFail, that item reports the error and the rest of the request goes on. |
params | array or object | No | Alias for values. Sending both is accepted when they hold the same value; different contents are refused with 400 VALUES_CONFLICT, and an explicit null counts as a value. |
valuesBatch | array | No | Batch bind parameters: one entry per execution of the statement; each entry follows the same rules as values. |
noFail | boolean | No | When true, a failure on this statement does not abort the transaction. |
Response
Section titled “Response”{ "results": [ { "success": true, "rowsUpdated": 0, "resultHeaders": [], "resultSet": [] }, { "success": true, "rowsUpdated": 1, "resultHeaders": [], "resultSet": [] } ]}{ "code": "INVALID_PARAMETERS", "error": "Invalid request parameters", "reqIdx": 1}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INVALID_PARAMETERS | Invalid request parameters | A parameter or the request body is missing, malformed or out of range. This is the default code for a 400 that has no more specific one. | Check parameter types and values against the API specification; the error text names the problem |
INVALID_DB_PATH | Invalid database path | The db/path value is empty, relative, outside /hoody/databases (unless the server allows any absolute path), contains ? or #, escapes via a symlink, or names something that is not a regular file | Provide an absolute path under /hoody/databases, or a bare name / ./name shorthand (resolved to /hoody/databases/*.db) |
DATABASE_NOT_FOUND | Database file does not exist | The database file does not exist and this request does not create it | Check the path, create the database first, or pass create_db_if_missing=true where the operation supports it |
INVALID_SQLITE_HEADER | Not a valid SQLite database | The file exists but is empty or does not start with the SQLite header | Point db at a SQLite database file |
PATH_IS_DIRECTORY | Path is a directory | Expected a database file but the path is a directory. Only reading, writing, deleting and checking a single key and listing keys (without at_timestamp) accept a directory, given as an absolute path (directory mode) | Use a .db file path |
SQL_ERROR | SQL statement failed | SQLite rejected the statement: syntax error, unknown table or column, datatype mismatch, value too big. The error text is SQLite’s own message | Fix the SQL; retrying the same statement fails the same way |
SQL_BIND_ERROR | Bind parameters do not match the statement | Wrong number of positional values, a missing named value, a named value whose name does not start with a letter, or a value that cannot be bound | Send one scalar per placeholder: an array for ?, ?NNN and $NNN, an object for :name, @name and $name (each name starting with a letter) |
UNKNOWN_FIELD | Unknown key on a transaction item | A transaction item carries a key that is not query, statement, sql, values, params, valuesBatch or noFail. reqIdx names the item; nothing ran | Remove or rename the key: bind values go in values (alias params), SQL in query or statement |
VALUES_CONFLICT | values and params disagree | A transaction item sent both values and its alias params, with different contents. reqIdx names the item; nothing ran | Send the bind values once, in values or in params |
{ "code": "SQL_CONSTRAINT", "error": "UNIQUE constraint failed: users.email", "reqIdx": 1}| Error Code | Title | Description | Resolution |
|---|---|---|---|
CONFLICT | Conflict | The request conflicts with the current state. Default code for a 409 that has no more specific one | Read the current state and retry with a request that matches it |
SQL_CONSTRAINT | Constraint violation | A statement violated a UNIQUE, PRIMARY KEY, FOREIGN KEY, NOT NULL or CHECK constraint; the whole transaction was rolled back (unless the item had noFail). A conflict that rolls back the transaction itself (INSERT OR ROLLBACK, RAISE(ROLLBACK) in a trigger) ends the request even under noFail, and nothing is committed | Change the data or the statement; a retry of the same request fails the same way |
DATABASE_READONLY | Database is read-only | SQLite refused a write because the database cannot be written | Check the database file permissions |
{ "code": "RESPONSE_TOO_LARGE", "error": "The result would exceed the response size limit", "reqIdx": 0}| Error Code | Title | Description | Resolution |
|---|---|---|---|
PAYLOAD_TOO_LARGE | Request too large | The request exceeds a size or item-count limit | Split the request |
RESPONSE_TOO_LARGE | Result too large | The result would exceed the response size limit; nothing was committed | Narrow the query or page through it with LIMIT/OFFSET |
{ "code": "DATABASE_ERROR", "error": "An internal database error occurred", "reqIdx": 0}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INTERNAL_ERROR | Internal server error | An unexpected server-side failure. Default code for a 500 that has no more specific one; details are in the server log | Retry later; report it if it persists |
DATABASE_ERROR | Database fault | SQLite failed for a reason that is not the request: I/O error, corruption, disk full, a file that cannot be opened. The message is sanitised; details are in the server log | Check the server log, disk space and the database file; the database may need repair |
{ "code": "DATABASE_BUSY", "error": "The database was locked by another writer", "reqIdx": -1}| Error Code | Title | Description | Resolution |
|---|---|---|---|
SERVICE_UNAVAILABLE | Service unavailable | The service could not complete the request right now. Default code for a 503 that has no more specific one | Retry after a short delay |
REQUEST_TIMEOUT | Request deadline exceeded | The request did not finish before its deadline (the timeout parameter, default 30 s); nothing was committed | Retry, or raise timeout (max 300) |
DATABASE_BUSY | Database busy | The database was locked by another writer or in use for longer than the request could wait; nothing was changed | Retry after a short delay |
SDK usage
Section titled “SDK usage”curl -X POST "https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com/api/v1/sqlite/db?db=my-database.db&create_db_if_missing=true" \ -H "Content-Type: application/json" \ -d '{ "resultFormat": "json", "transaction": [ { "statement": "CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)", "noFail": true }, { "statement": "INSERT INTO users (name, email) VALUES (?, ?)", "values": ["Ada", "ada@example.com"] } ] }'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
const result = await client.sqlite.sql.runTransaction( { resultFormat: "json", transaction: [ { statement: "CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)", noFail: true }, { statement: "INSERT INTO users (name, email) VALUES (?, ?)", values: ["Ada", "ada@example.com"] } ] }, { db: "my-database.db", create_db_if_missing: true });Create new SQLite database
Section titled “Create new SQLite database”POST /api/v1/sqlite/db/create
Section titled “POST /api/v1/sqlite/db/create”Create a new empty SQLite database with optional KV store initialization.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
path | query | string | Yes | Database path (absolute path, bare name, or ./name shorthand resolved to /hoody/databases/*.db) |
init_kv | query | boolean | No | Initialize KV store tables. Default: false |
kv_table | query | string | No | Custom KV table name. Default: "kv_store" |
timeout | query | integer | No | Deadline for this request, in whole seconds, clamped to [1, 300]. Once it passes, a long operation stops at its next checkpoint rather than being cut off mid-step; a step already running, such as a filesystem scan or a wait for another writer, finishes first. A request that has not finished by then answers 503 REQUEST_TIMEOUT, except that a write which has already committed still returns its success. A value that is not a whole number is ignored and the default applies. This is a server-side deadline, not a client transport timeout. Omitted, the server default applies (30 seconds unless the deployment overrides it). Default: 30 |
Response
Section titled “Response”{ "success": true, "path": "my-database.db", "message": "Database created successfully", "size": 16384, "kv_table": "kv_store", "tables": ["_query_history", "kv_store", "kv_store_history"]}{ "code": "INVALID_DB_PATH", "error": "The provided database path is invalid or inaccessible"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INVALID_PARAMETERS | Invalid request parameters | A parameter or the request body is missing, malformed or out of range. This is the default code for a 400 that has no more specific one. | Check parameter types and values against the API specification; the error text names the problem |
INVALID_DB_PATH | Invalid database path | The db/path value is empty, relative, outside /hoody/databases (unless the server allows any absolute path), contains ? or #, escapes via a symlink, or names something that is not a regular file | Provide an absolute path under /hoody/databases, or a bare name / ./name shorthand (resolved to /hoody/databases/*.db) |
{ "code": "FORBIDDEN", "error": "The new file resolved outside /hoody/databases through a symlink and was removed again", "path": "my-database.db"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
FORBIDDEN | Forbidden | The request is not allowed: a path that escapes the database directory, or a request refused by the Source IP Guard (Hoody Kit programs are reached through their URLs only) | Use a path inside the permitted directory, and reach the program through its URL |
{ "code": "DATABASE_EXISTS", "error": "A file already exists at the requested path", "exists": true, "path": "my-database.db"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
CONFLICT | Conflict | The request conflicts with the current state. Default code for a 409 that has no more specific one | Read the current state and retry with a request that matches it |
DATABASE_EXISTS | Database already exists | A file already exists at the requested path | Choose another path, or use the existing database |
{ "code": "DATABASE_ERROR", "error": "An internal database error occurred"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INTERNAL_ERROR | Internal server error | An unexpected server-side failure. Default code for a 500 that has no more specific one; details are in the server log | Retry later; report it if it persists |
DATABASE_ERROR | Database fault | SQLite failed for a reason that is not the request: I/O error, corruption, disk full, a file that cannot be opened. The message is sanitised; details are in the server log | Check the server log, disk space and the database file; the database may need repair |
{ "code": "SERVICE_UNAVAILABLE", "error": "The service could not complete the request right now"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
SERVICE_UNAVAILABLE | Service unavailable | The service could not complete the request right now. Default code for a 503 that has no more specific one | Retry after a short delay |
REQUEST_TIMEOUT | Request deadline exceeded | The request did not finish before its deadline (the timeout parameter, default 30 s); nothing was committed | Retry, or raise timeout (max 300) |
DATABASE_BUSY | Database busy | The database was locked by another writer or in use for longer than the request could wait; nothing was changed | Retry after a short delay |
SDK usage
Section titled “SDK usage”curl -X POST "https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com/api/v1/sqlite/db/create?path=my-database.db&init_kv=true&kv_table=kv_store"import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
const result = await client.sqlite.databases.create({ path: 'my-database.db', init_kv: true, kv_table: 'kv_store',});Delete SQLite database
Section titled “Delete SQLite database”DELETE /api/v1/sqlite/db
Section titled “DELETE /api/v1/sqlite/db”Permanently delete a SQLite database file, together with its -wal, -shm and -journal files and everything stored in it (tables, KV stores, query and KV history). Requests already running against the database finish first; new requests wait until the deletion is done and then find no database. Only SQLite database files can be deleted: a directory-mode KV store (a directory) is refused, and so is any file without a SQLite header. Cannot be undone.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
db | query | string | Yes | Database path (absolute path, bare name, or ./name shorthand resolved to /hoody/databases/*.db) |
timeout | query | integer | No | Deadline for this request, in whole seconds, clamped to [1, 300]. Once it passes, a long operation stops at its next checkpoint rather than being cut off mid-step; a step already running, such as a filesystem scan or a wait for another writer, finishes first. A request that has not finished by then answers 503 REQUEST_TIMEOUT, except that a write which has already committed still returns its success. A value that is not a whole number is ignored and the default applies. This is a server-side deadline, not a client transport timeout. Omitted, the server default applies (30 seconds unless the deployment overrides it). Default: 30 |
Response
Section titled “Response”{ "success": true, "path": "my-database.db", "files_removed": 4, "message": "Database deleted successfully"}{ "code": "INVALID_DB_PATH", "error": "The provided database path is invalid or inaccessible"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INVALID_PARAMETERS | Invalid request parameters | A parameter or the request body is missing, malformed or out of range. This is the default code for a 400 that has no more specific one. | Check parameter types and values against the API specification; the error text names the problem |
INVALID_DB_PATH | Invalid database path | The db/path value is empty, relative, outside /hoody/databases (unless the server allows any absolute path), contains ? or #, escapes via a symlink, or names something that is not a regular file | Provide an absolute path under /hoody/databases, or a bare name / ./name shorthand (resolved to /hoody/databases/*.db) |
INVALID_SQLITE_HEADER | Not a valid SQLite database | The file exists but is empty or does not start with the SQLite header | Point db at a SQLite database file |
PATH_IS_DIRECTORY | Path is a directory | Expected a database file but the path is a directory. Only reading, writing, deleting and checking a single key and listing keys (without at_timestamp) accept a directory, given as an absolute path (directory mode) | Use a .db file path |
{ "code": "DATABASE_NOT_FOUND", "error": "The database file does not exist"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
DATABASE_NOT_FOUND | Database file does not exist | The database file does not exist and this request does not create it | Check the path, create the database first, or pass create_db_if_missing=true where the operation supports it |
{ "code": "INTERNAL_ERROR", "error": "An internal server error occurred"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INTERNAL_ERROR | Internal server error | An unexpected server-side failure. Default code for a 500 that has no more specific one; details are in the server log | Retry later; report it if it persists |
DELETE_INCOMPLETE | Database delete incomplete | A file beside the database (-wal, -shm, -journal) or the database itself could not be removed. Sidecars are removed first, so the database file is still in place; files_removed says how many files were removed | Retry the delete; it starts over and finishes the job. If it keeps failing, check the file permissions in the database directory |
{ "code": "SERVICE_UNAVAILABLE", "error": "The service could not complete the request right now"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
SERVICE_UNAVAILABLE | Service unavailable | The service could not complete the request right now. Default code for a 503 that has no more specific one | Retry after a short delay |
REQUEST_TIMEOUT | Request deadline exceeded | The request did not finish before its deadline (the timeout parameter, default 30 s); nothing was committed | Retry, or raise timeout (max 300) |
DATABASE_BUSY | Database busy | The database was locked by another writer or in use for longer than the request could wait; nothing was changed | Retry after a short delay |
SDK usage
Section titled “SDK usage”curl -X DELETE "https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com/api/v1/sqlite/db?db=my-database.db"import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
const result = await client.sqlite.databases.delete({ db: 'my-database.db',});Run database maintenance
Section titled “Run database maintenance”POST /api/v1/sqlite/maintenance
Section titled “POST /api/v1/sqlite/maintenance”Run a maintenance operation that cannot execute inside the transactional /db endpoint: wal_checkpoint_truncate (PRAGMA wal_checkpoint(TRUNCATE)), vacuum_into (VACUUM INTO dest_path; dest_path is jailed like the db parameter and must not already exist; missing parent folders of dest_path are created), quick_check (PRAGMA quick_check; result carries the first result row, ok on a healthy database), restore (replace the whole content of the database with the SQLite database at src_path, such as a vacuum_into copy, then start a new KV change-capture epoch as reset_changes does; it runs as one write transaction, so open readers and later requests see either the old or the restored content, never a mix; a restore refused or failed before its copy commits, or out of time, changes nothing; src_path is jailed like the db parameter and is only read; a source whose page size differs from the database’s is refused with 400), or reset_changes (start a new KV change-capture epoch: every change cursor and every KV ETag of the database stops matching, so clients re-read). To restore a backup, use restore: a file copied over a database that is open here is not seen consistently, because its old -wal file and the cached pages stay in use. The operation runs directly on the database connection, fenced from concurrent query handlers. The database is never created; a missing file returns 404. Long VACUUMs can extend the request deadline via the ?timeout= query parameter (clamped to 5m).
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
db | query | string | Yes | Database path (absolute path, bare name, or ./name shorthand resolved to /hoody/databases/*.db) |
timeout | query | integer | No | Deadline for this request, in whole seconds, clamped to [1, 300]. Once it passes, a long operation stops at its next checkpoint rather than being cut off mid-step; a step already running, such as a filesystem scan or a wait for another writer, finishes first. A request that has not finished by then answers 503 REQUEST_TIMEOUT, except that a write which has already committed still returns its success. A value that is not a whole number is ignored and the default applies. This is a server-side deadline, not a client transport timeout. Omitted, the server default applies (30 seconds unless the deployment overrides it). Default: 30 |
Request body
Section titled “Request body”| Field | Type | Required | Description |
|---|---|---|---|
op | string | Yes | Which maintenance operation to run. One of wal_checkpoint_truncate, vacuum_into, quick_check, reset_changes, restore. |
dest_path | string | No | Destination file for vacuum_into. Required for that operation and ignored by the others. Jailed like the db parameter, and it must not already exist. |
src_path | string | No | Source file for restore: an existing SQLite database, such as a vacuum_into copy. Required for that operation and ignored by the others. Jailed like the db parameter, and it must not be the database itself. It is only read. |
Response
Section titled “Response”{ "op": "wal_checkpoint_truncate", "result": "ok"}{ "code": "INVALID_PARAMETERS", "error": "Invalid op, db path, dest_path or src_path"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INVALID_PARAMETERS | Invalid request parameters | A parameter or the request body is missing, malformed or out of range. This is the default code for a 400 that has no more specific one. | Check parameter types and values against the API specification; the error text names the problem |
INVALID_DB_PATH | Invalid database path | The db/path value is empty, relative, outside /hoody/databases (unless the server allows any absolute path), contains ? or #, escapes via a symlink, or names something that is not a regular file | Provide an absolute path under /hoody/databases, or a bare name / ./name shorthand (resolved to /hoody/databases/*.db) |
DATABASE_NOT_FOUND | Database file does not exist | The database file does not exist and this request does not create it | Check the path, create the database first, or pass create_db_if_missing=true where the operation supports it |
INVALID_SQLITE_HEADER | Not a valid SQLite database | The file exists but is empty or does not start with the SQLite header | Point db at a SQLite database file |
PATH_IS_DIRECTORY | Path is a directory | Expected a database file but the path is a directory. Only reading, writing, deleting and checking a single key and listing keys (without at_timestamp) accept a directory, given as an absolute path (directory mode) | Use a .db file path |
{ "code": "DATABASE_NOT_FOUND", "error": "The database file does not exist"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
NOT_FOUND | Not found | The requested item does not exist. Default code for a 404 that has no more specific one | Check the identifiers in the request |
DATABASE_NOT_FOUND | Database file does not exist | The database file does not exist and this request does not create it | Check the path, create the database first, or pass create_db_if_missing=true where the operation supports it |
{ "code": "CONFLICT", "error": "vacuum_into destination already exists, or checkpoint blocked by another process"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
CONFLICT | Conflict | The request conflicts with the current state. Default code for a 409 that has no more specific one | Read the current state and retry with a request that matches it |
{ "code": "DATABASE_ERROR", "error": "An internal database error occurred"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INTERNAL_ERROR | Internal server error | An unexpected server-side failure. Default code for a 500 that has no more specific one; details are in the server log | Retry later; report it if it persists |
DATABASE_ERROR | Database fault | SQLite failed for a reason that is not the request: I/O error, corruption, disk full, a file that cannot be opened. The message is sanitised; details are in the server log | Check the server log, disk space and the database file; the database may need repair |
{ "code": "SERVICE_UNAVAILABLE", "error": "The service could not complete the request right now"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
SERVICE_UNAVAILABLE | Service unavailable | The service could not complete the request right now. Default code for a 503 that has no more specific one | Retry after a short delay |
REQUEST_TIMEOUT | Request deadline exceeded | The request did not finish before its deadline (the timeout parameter, default 30 s); nothing was committed | Retry, or raise timeout (max 300) |
DATABASE_BUSY | Database busy | The database was locked by another writer or in use for longer than the request could wait; nothing was changed | Retry after a short delay |
SDK usage
Section titled “SDK usage”curl -X POST "https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com/api/v1/sqlite/maintenance?db=my-database.db&timeout=60" \ -H "Content-Type: application/json" \ -d '{ "op": "wal_checkpoint_truncate" }'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
const result = await client.sqlite.databases.runMaintenance( { op: 'wal_checkpoint_truncate' }, { db: 'my-database.db', timeout: 60 });