Table declarations and blob references
Declare table column types and attach binary content through blob references.
Table declarations and blob references
Tables can declare column types before the first record arrives. Additional columns continue to be inferred. A declared type cannot be replaced by inference or background indexed-file publication. Schema locking, destructive edits, and conversion of existing column data are not supported.
Define a table
Send PUT /accounts/{accountId}/tables/{tableName} with a user JWT or personal access token containing account-management capability. The caller must currently be an account administrator or global administrator.
{
"columns": [
{ "name": "message", "type": "String" },
{ "name": "screenshot", "type": "BlobReference" },
{ "name": "count", "type": "Int64" }
]
}The response lists the merged columns with their canonical type names and isDeclared flags. Repeating a definition is safe. Existing columns can be marked declared when their stored type agrees; incompatible changes return HTTP 409. Existing inferred columns and retention settings remain intact.
Equivalent management commands use the normal Kusto query API:
.create table 'logs' (message:string, screenshot:BlobReference, count:Int64).create-merge table 'logs' (anotherColumn:string).create table creates a table or accepts an existing matching declaration. Use .create-merge table to add declarations to an existing table. Commands require a query scope permitting control commands in addition to the same account-management capability and administrator membership as HTTP.
The built-in timestamp column has type DateTime and is added automatically. Declared columns are nullable and may be omitted. Inflow metadata fields beginning with __INFLOW_ belong to the server and cannot be declared by clients. Supported declared types are String, Int64, UInt64, Float64, DateTime, Timespan, Boolean, Int128 (UUID), Json, and BlobReference; existing query type aliases are accepted.
For full syntax, see .create table and .create-merge table.
CSV and TSV column types
Both formats accept newlines inside double-quoted fields and preserve the embedded line endings. Escape a double quote inside a quoted field as "". A newline outside quotes starts a new record. Unterminated quoted fields are rejected, and record size limits apply to the entire record across all its physical lines. Input errors identify the physical line where the record starts.
CSV and TSV uploads honor declared column types. Undeclared columns use the first nonempty value to infer a type. If a date-like column also contains text placeholders such as ##/##/####, declare it as String before uploading to preserve both the dates and placeholders:
.create table 'registrations' (registr_dt:string)Other columns continue to be inferred. Input conversion errors identify the column and input line; upload progress measures bytes read for transmission, rather than rows accepted by storage.
To retain a DateTime column and import redacted dates as null, use logsh upload --null-value "##/##/####" <table> <file.tsv>. This option requires a backend version supporting null markers. HTTP clients can pass repeated nullValue query parameters to the CSV/TSV inflow endpoint. Each marker matches exact field text after removing surrounding quotes, before type inference or conversion, and applies to every matching field. Matching fields are omitted as null; their rows are retained. Repeating the option supports multiple markers, including an empty string. Without this option, existing parsing behavior applies, and malformed dates that do not match a marker still produce errors. Existing declared column types are preserved.
Inspect or delete tables
Run each command separately through the query editor or normal query API in the target account, using a query scope that permits control commands:
.show tables.show table 'logs'.drop table 'logs'.show tables lists table metadata; .show table lists columns and types. .drop table deletes the table and retires its indexed files. Stop producers first because subsequent ingestion can create the table again. Blob reference release and physical reclamation happen asynchronously; other live references continue protecting shared blobs.
Ingest a blob reference
- Declare the destination column as
BlobReference. - Upload and complete the blob for that table.
- Send the blob UUID as a string in the declared column to
PUT /inflow/{accountId}:
[
{
"ingestionId": "aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee",
"schema": "logs",
"timestamp": "2026-10-26T15:00:00Z",
"data": {
"message": "Request failed",
"screenshot": "11111111-2222-4333-8444-555555555555",
"count": 9007199254740993,
"additionalColumn": true
}
}
]UUID strings become blob references only in explicitly declared BlobReference columns. Other strings retain their existing inference behavior. Integer declarations parse the original JSON number without converting through floating point. Declared boolean columns accept JSON booleans; datetime, timespan, and UUID columns accept their invariant string representations. Structured JSON requires a declared Json column. Invalid declared values reject ingestion with a client error.
Every row with a non-null blob reference requires a nonempty ingestionId UUID. Keep it stable when retrying that row, along with its schema, timestamp, and client data. Generated inflow metadata is retained from the first accepted attempt. The same identity with changed client input returns HTTP 409. Top-level data-field ordering does not affect the identity; number tokens are preserved exactly.
The server checks destination write permissions and either ownership of the completed upload for that table or read access to an existing live reference being reused. Upload IDs cannot be used to attach another user's unreferenced content. Records acquire durable protections before successful acceptance; searchable publication follows asynchronously.
Poll GET /inflow/{accountId}/{schema}/records/{ingestionId} with the same user to inspect preparing, accepted, protected, published, completed, or abandoned state. The identity is scoped to the account and authenticated user. Once searchable, query results expose the blob reference and its download URL; downloads require read access to a referencing table.
Batch ingestion retains existing partial-success behavior. Stable identities prevent duplicate acceptance of retried blob-bearing rows; ordinary rows keep their existing ingestion semantics. CSV/TSV honor declared ordinary scalar types, but non-null blob attachments require JSON and ingestionId.
Upgrade compatibility
Upgrade the catalog, API/inflow, and executor roles before using declarations. Existing fields migrate as inferred; stored files and persisted primitive type identifiers remain unchanged. Existing blob durability envelopes omit the new optional client-input digest when absent, preserving recovery of prior operations. Public binary readiness remains gated by the remaining compaction and journal-lifecycle work.