Skip to main content

Nova API

The operations Nova tags public: the stable surface for tools and scripts. Generated from reference-src/nova-openapi.json.

Every non-2xx answer uses the body shape {"error": "<sentence>"}.

/api/auth​

GET /api/auth/config​

Operation id auth_config: in Python client.call("auth_config", …), on the command line stardelt api auth_config.

How to sign in from a command line. Outside sign-in like the rest of /api/auth/*: it names the identity provider and the public client, never a secret.

How to sign in from a command line: the identity provider's address and the public client for the device flow. It answers without sign-in and carries no secret.

Responses

CodeDescription
200The sign-in mode and, in oidc mode, the issuer and the device client

/api/catalog​

GET /api/catalog/catalogs​

Operation id catalogs_list: in Python client.call("catalogs_list", …), on the command line stardelt api catalogs_list.

The catalogs the caller may browse, each with its Lakekeeper warehouse id and its medallion layer when it has one, plus the catalog a request without ?catalog= uses. The order is bronze, silver, gold first (in that order), then the rest alphabetically; layer is stardelt.io/layer from the catalog's Catalog resource, else the catalog's own name when that is bronze, silver or gold, and absent otherwise. The list is what the engine's SHOW CATALOGS shows the caller (through the caller's own token), minus the engine's own catalogs (system, information_schema, memory, tpch, tpcds, jmx); it is cached for 30 s (the layer for 60 s). Every other /api/catalog/* route takes the same ?catalog=; a catalog the caller may not browse answers 403 there and is never proxied. Empty when no catalog is open to the caller. demo in demo mode.

Responses

CodeDescription
200The browsable catalogs and the default one
502The query engine or the catalog is not reachable
503A catalog the engine lists has no warehouse

GET /api/catalog/metrics​

Operation id catalog_metrics_get: in Python client.call("catalog_metrics_get", …), on the command line stardelt api catalog_metrics_get.

What is in each catalog the caller may browse: schemas, tables, rows, bytes on the object store and the newest commit, plus the sums. Rows are ordered bronze, silver, gold first, then by name, and carry layer when the catalog is one of the three (the stardelt.io/layer label, else the catalog's own name). The catalog list is the caller's own SHOW CATALOGS (GET /api/catalog/catalogs); a catalog the caller may not browse is never read and never appears. The numbers come from the catalog's Iceberg metadata (the current snapshot's total-records and total-files-size), so the endpoint runs no query, and they are read in the background every five minutes (NOVA_METRICS_REFRESH_SECS): a request answers from the last numbers at once, whatever their age, and updatedAt says when they were read. A catalog with no numbers yet is answered with zeros and pending: true while one background scan reads it; totals leave pending rows out. At most 200 tables per catalog are loaded: truncated says the rows and bytes are a sample while tables stays exact. A catalog whose scan failed keeps its last numbers and carries an error sentence (zeros for one that never answered); only an unreachable engine or catalog fails the request with 502. 403 when no catalog is open to the caller. Never cached by the client.

Responses

CodeDescription
200One entry per browsable catalog, medallion layers first, then by name, with the totals
403No catalog is open to the caller
502The query engine is not reachable, so the catalogs cannot be named

POST /api/catalog/metrics/refresh​

Operation id catalog_metrics_refresh: in Python client.call("catalog_metrics_refresh", …), on the command line stardelt api catalog_metrics_refresh.

Read every catalog again now, instead of waiting for the next background round (every five minutes). Administrator only: it is an operator action on the whole warehouse, and main puts the same require_admin in front of it as /api/apps, so a non-administrator answers 403. Answers 202 — the round runs in the background, one at a time, and the page reads the new numbers with its next GET /api/catalog/metrics. At most one per minute: a second call answers 429 with the sentence the numbers were refreshed less than a minute ago.

Responses

CodeDescription
202The round is running; the numbers follow on the next read
403Not an administrator
429The numbers were refreshed less than a minute ago

GET /api/catalog/namespaces​

Operation id namespaces_list: in Python client.call("namespaces_list", …), on the command line stardelt api namespaces_list.

List one catalog's namespaces (schemas). Each namespace is a list of levels. ?catalog= picks the catalog; omitted, the default one is used. A catalog the caller may not browse answers 403, a name no catalog knows 404.

Parameters

NameInTypeRequiredDescription
catalogquerystringNoCatalog to work in (warehouse, finance, …). Omitted, the default catalog is used.

Responses

CodeDescription
200Namespaces in the catalog
400The catalog name is not lower-case letters and digits
403The caller may not browse that catalog
404No such catalog
502The catalog or the engine is not reachable
503The configured warehouse does not exist

GET /api/catalog/namespaces/{ns}/tables​

Operation id tables_list: in Python client.call("tables_list", …), on the command line stardelt api tables_list.

List tables in one namespace of one catalog. ?catalog= picks the catalog; omitted, the default one is used.

Parameters

NameInTypeRequiredDescription
catalogquerystringNoCatalog to work in (warehouse, finance, …). Omitted, the default catalog is used.
nspathstringYesNamespace (schema) name

Responses

CodeDescription
200Table identifiers in the namespace
400The name is not a single path segment, or the catalog name is invalid
403The caller may not browse that catalog
404No such catalog
502The catalog or the engine is not reachable

GET /api/catalog/search​

Operation id catalog_search: in Python client.call("catalog_search", …), on the command line stardelt api catalog_search.

Find tables by name in one catalog. q is one or more words; a table matches when its namespace.table contains every word, in any case. The catalog's namespaces and tables are listed the way namespaces_list and tables_list list them, so the answer shows only what the caller may browse; only names are searched (single-level namespaces), not comments or tags. ?catalog= picks the catalog; omitted, the default one is used. At most 100 namespaces are read per request: truncated says when the answer is a prefix.

Parameters

NameInTypeRequiredDescription
catalogquerystringNoCatalog to search (warehouse, finance, …). Omitted, the default catalog is used.
limitqueryintegerNoMost matches to return, 1 to 100; 20 when omitted.
qquerystringNoWords to look for in namespace.table, separated by spaces; every word must be there. 1 to 100 characters.

Responses

CodeDescription
200The matching tables
400q is missing or longer than 100 characters, or the catalog name is invalid
403The caller may not browse that catalog
404No such catalog
502The catalog or the engine is not reachable

GET /api/catalog/tables/{ns}/{tbl}​

Operation id table_metadata: in Python client.call("table_metadata", …), on the command line stardelt api table_metadata.

Iceberg metadata for one table (metadata location plus the metadata document). ?catalog= picks the catalog; omitted, the default one is used.

Parameters

NameInTypeRequiredDescription
catalogquerystringNoCatalog to work in (warehouse, finance, …). Omitted, the default catalog is used.
nspathstringYesNamespace (schema) name
tblpathstringYesTable name

Responses

CodeDescription
200The table metadata document
400A name is not a single path segment, or the catalog name is invalid
403The caller may not browse that catalog
404No such catalog or no such table
502The catalog or the engine is not reachable

PUT /api/catalog/tables/{ns}/{tbl}/columns/{column}/description​

Operation id column_description_update: in Python client.call("column_description_update", …), on the command line stardelt api column_description_update.

Set or remove one column's description. The column must be a field of the current schema, matched exactly. Nova runs COMMENT ON COLUMN <catalog>.<ns>.<tbl>.<column> through Trino as the caller (source nova-catalog), because a column description lives in a new Iceberg schema and only the engine writes it. ?catalog= picks the catalog. Same 4,000-character limit and null rule as the table description. The interim rule is enforced by Nova; the catalog does not enforce it yet. Demo mode answers 503.

Parameters

NameInTypeRequiredDescription
catalogquerystringNoCatalog to work in (warehouse, finance, …). Omitted, the default catalog is used.
columnpathstringYesColumn name, exactly as the current schema spells it
nspathstringYesNamespace (schema) name
tblpathstringYesTable name

Request body

FieldTypeRequiredDefaultDescriptionConstraints
descriptionstring (nullable)NoNew column description (null removes it).

Responses

CodeDescription
200The table's documentation after the change, freshly loaded
400The body is invalid, or the catalog name is invalid
403The caller may not browse that catalog, or only the owner group and administrators may edit
404No such catalog, no such table or no such column in the current schema
502The catalog or the engine is not reachable
503Demo mode

GET /api/catalog/tables/{ns}/{tbl}/documentation​

Operation id table_documentation_get: in Python client.call("table_documentation_get", …), on the command line stardelt api table_documentation_get.

A table's documentation: the Iceberg comment as description, the stardelt.tags/stardelt.owner/stardelt.domain custom properties, and the description and type of every column of the current schema. ?catalog= picks the catalog. canEdit/editRule apply Nova's interim rule: until the catalog enforces permissions, Nova decides who may edit, and anyone who reaches Lakekeeper or Trino directly can still change these properties. Demo mode answers 503.

Parameters

NameInTypeRequiredDescription
catalogquerystringNoCatalog to work in (warehouse, finance, …). Omitted, the default catalog is used.
nspathstringYesNamespace (schema) name
tblpathstringYesTable name

Responses

CodeDescription
200The table's documentation
400A name is not a single path segment, or the catalog name is invalid
403The caller may not browse that catalog
404No such catalog or no such table
502The catalog is not reachable or did not answer
503Demo mode

PUT /api/catalog/tables/{ns}/{tbl}/documentation​

Operation id table_documentation_update: in Python client.call("table_documentation_update", …), on the command line stardelt api table_documentation_update.

Set or remove a table's description, tags, owner and domain in one Lakekeeper commit. ?catalog= picks the catalog. Every key is optional: absent changes nothing, null (or an empty tag list, or a blank description) removes the property, and a write with nothing to change sends no commit. Description at most 4,000 characters; at most 20 unique tags ^[a-z0-9][a-z0-9_-]{0,31}$; owner a group name; domain ^[a-z0-9][a-z0-9_-]{0,39}$. An ownerless table may be edited by anybody, but the request must then set owner to one of the caller's own groups. The interim rule is enforced by Nova; the catalog does not enforce it yet — anyone who reaches Lakekeeper or Trino directly can still change these properties. Edits of one table are serialised by Nova and a contended commit is retried; a catalog that stays busy answers 502 the catalog is busy, try again. Demo mode answers 503.

Parameters

NameInTypeRequiredDescription
catalogquerystringNoCatalog to work in (warehouse, finance, …). Omitted, the default catalog is used.
nspathstringYesNamespace (schema) name
tblpathstringYesTable name

Request body

FieldTypeRequiredDefaultDescriptionConstraints
descriptionstring (nullable)NoNew table description (null removes it).
domainstring (nullable)NoNew domain (null removes it).
ownerstring (nullable)NoNew owner group (null removes it).
tagsarray of string (nullable)NoNew tag list (null or [] removes them).

Responses

CodeDescription
200The documentation after the change, freshly loaded
400The body is invalid, the catalog name is invalid, or the table has no owner and this request does not claim it
403The caller may not browse that catalog, or only the owner group and administrators may edit
404No such catalog or no such table
502The catalog is not reachable, did not answer, or stayed busy
503Demo mode

GET /api/catalog/tables/{ns}/{tbl}/history​

Operation id table_history: in Python client.call("table_history", …), on the command line stardelt api table_history.

The table's last 100 snapshots, newest first: committed_at, snapshot_id, parent_id, operation, summary. Snapshot and parent ids are strings so that all 64 bits survive JSON in a browser.

Parameters

NameInTypeRequiredDescription
catalogquerystringNoCatalog to work in (warehouse, finance, …). Omitted, the default catalog is used.
nspathstringYesNamespace (schema) name
tblpathstringYesTable name

Responses

CodeDescription
200One row per snapshot
400A name is not a single path segment, or the engine refused the statement
403The caller may not browse that catalog
404No such catalog
502The catalog or the engine is not reachable

POST /api/catalog/tables/{ns}/{tbl}/rollback​

Operation id table_rollback: in Python client.call("table_rollback", …), on the command line stardelt api table_rollback.

Move the table's current pointer back to an earlier snapshot (ALTER TABLE … EXECUTE rollback_to_snapshot), run as the caller, so the engine's permissions decide. Nothing is deleted: the newer snapshots stay until they are expired. Only the catalog owner group or an administrator may roll back; others are refused with that sentence before the engine is called.

Parameters

NameInTypeRequiredDescription
catalogquerystringNoCatalog to work in (warehouse, finance, …). Omitted, the default catalog is used.
nspathstringYesNamespace (schema) name
tblpathstringYesTable name

Request body

FieldTypeRequiredDefaultDescriptionConstraints
snapshot_idstringYesThe snapshot to return to, as a string of digits (an Iceberg id has 64 bits; a JSON number is accepted too but a browser rounds it).

Responses

CodeDescription
200The engine's answer to the statement
400A name or the body is not valid, the snapshot is unknown, or the engine refused the statement
403Only the catalog owner group or administrators may roll back
404No such catalog
502The catalog or the engine is not reachable

GET /api/catalog/warehouse​

Operation id warehouse_info: in Python client.call("warehouse_info", …), on the command line stardelt api warehouse_info.

The resolved warehouse name and id (demo in demo mode).

Responses

CodeDescription
200Warehouse name and id
502The catalog is not reachable
503The configured warehouse does not exist

/api/connections​

GET /api/connections​

Operation id connections_list: in Python client.call("connections_list", …), on the command line stardelt api connections_list.

The caller's connections: their own, their groups', and — for administrators — the managed ones. Values are never part of an answer, only the field names.

Responses

CodeDescription
200Personal, then group, then managed connections, each sorted by name
401No token to sign in to the secret store with
502The secret store or the cluster did not answer
503The secret store is not configured

POST /api/connections​

Operation id connections_create: in Python client.call("connections_create", …), on the command line stardelt api connections_create.

Create a connection. The values in secret are written to the platform's secret store with the caller's own token and are never returned by any route.

Request body

FieldTypeRequiredDefaultDescriptionConstraints
configobject (map of string)No
namestringYes
ownerGroupstring (nullable)No
refreshIntervalstring (nullable)No
scopestringYespersonal, group, managed
secretobject (map of string)YesThe secret field values. Write-only: Nova stores them and never reads them back.
typestringYes
usableByarray of stringNo

Responses

CodeDescription
201The created connection, without values
400A sentence from the connection rules; never a value
401No token to sign in to the secret store with
403Not allowed for this scope
409A connection with this name already exists
502The secret store or the cluster did not answer
503The secret store is not configured, or no cluster connection

GET /api/connections/break-glass-log​

Operation id connections_break_glass_log: in Python client.call("connections_break_glass_log", …), on the command line stardelt api connections_break_glass_log.

The last 200 break-glass reads and refusals, newest first, for administrators. The rows carry the administrator, group, connection, reason and outcome — never a value. Cache-Control: no-store.

Responses

CodeDescription
200The most recent events, newest first
403Not an administrator
503The state store is not configured

DELETE /api/connections/group/{group}/{name}​

Operation id connections_group_delete: in Python client.call("connections_group_delete", …), on the command line stardelt api connections_group_delete.

Delete one of a group's connections. The value goes first, then the resource; if the resource delete fails the entry stays visible as not-ready and the delete can be retried.

Parameters

NameInTypeRequiredDescription
grouppathstringYesOwner group
namepathstringYesConnection name

Responses

CodeDescription
204Deleted
401No token to sign in to the secret store with
403Not a member of the group
404No such connection
502The secret store or the cluster did not answer
503The secret store is not configured, or no cluster connection

POST /api/connections/group/{group}/{name}/break-glass​

Operation id connections_group_break_glass: in Python client.call("connections_group_break_glass", …), on the command line stardelt api connections_group_break_glass.

Emergency read of a group connection by an administrator: a written reason, a notice on the Connection for the owner group, an audit row, and the values returned once. The answer is never stored and carries Cache-Control: no-store and Pragma: no-cache. Personal and managed connections are never readable.

Parameters

NameInTypeRequiredDescription
grouppathstringYesOwner group
namepathstringYesConnection name

Request body

FieldTypeRequiredDefaultDescriptionConstraints
reasonstringYes

Responses

CodeDescription
200The connection's fields, returned once and never stored; the owner group's notice is already written
400The reason is shorter than 20 characters; never a value
401No token to sign in to the secret store with
403Not an administrator, or the store refused the break-glass sign-in
404No such connection
429More than five break-glass reads by this administrator in ten minutes
502The secret store did not answer, or the notice on the Connection could not be written (nothing is returned then)
503The secret store is not configured

PUT /api/connections/group/{group}/{name}/secret​

Operation id connections_group_rotate: in Python client.call("connections_group_rotate", …), on the command line stardelt api connections_group_rotate.

Rotate some fields of one of a group's connections. Asked to write the fields that are sent; the others keep their value.

Parameters

NameInTypeRequiredDescription
grouppathstringYesOwner group
namepathstringYesConnection name

Request body

FieldTypeRequiredDefaultDescriptionConstraints
secretobject (map of string)YesThe fields to rotate; a managed connection sends every field.

Responses

CodeDescription
200The connection after the rotation, without values
400A sentence from the connection rules; never a value
401No token to sign in to the secret store with
403Not a member of the group
404No such connection
502The secret store or the cluster did not answer
503The secret store is not configured, or no cluster connection

DELETE /api/connections/managed/{name}​

Operation id connections_managed_delete: in Python client.call("connections_managed_delete", …), on the command line stardelt api connections_managed_delete.

Delete an administrator-managed connection. The value goes first, then the resource; if the resource delete fails the entry stays visible as not-ready and the delete can be retried.

Parameters

NameInTypeRequiredDescription
namepathstringYesConnection name

Responses

CodeDescription
204Deleted
401No token to sign in to the secret store with
403Managed connections are administrators' business
404No such connection
502The secret store or the cluster did not answer
503The secret store is not configured, or no cluster connection

POST /api/connections/managed/{name}/break-glass​

Operation id connections_managed_break_glass: in Python client.call("connections_managed_break_glass", …), on the command line stardelt api connections_managed_break_glass.

Always refused: break-glass covers group connections only. An administrator gets a 403 and an audit row; nothing is read.

Parameters

NameInTypeRequiredDescription
namepathstringYesConnection name

Request body

FieldTypeRequiredDefaultDescriptionConstraints
reasonstringYes

Responses

CodeDescription
401No token to sign in to the secret store with
403Break-glass covers group connections only, or the caller is not an administrator
503The secret store is not configured

PUT /api/connections/managed/{name}/secret​

Operation id connections_managed_rotate: in Python client.call("connections_managed_rotate", …), on the command line stardelt api connections_managed_rotate.

Rotate an administrator-managed connection. Nobody can read it back, so every field must be sent.

Parameters

NameInTypeRequiredDescription
namepathstringYesConnection name

Request body

FieldTypeRequiredDefaultDescriptionConstraints
secretobject (map of string)YesThe fields to rotate; a managed connection sends every field.

Responses

CodeDescription
200The connection after the rotation, without values
400A sentence from the connection rules; never a value
401No token to sign in to the secret store with
403Managed connections are administrators' business
404No such connection
502The secret store or the cluster did not answer
503The secret store is not configured, or no cluster connection

DELETE /api/connections/personal/{name}​

Operation id connections_personal_delete: in Python client.call("connections_personal_delete", …), on the command line stardelt api connections_personal_delete.

Delete one of the caller's own connections.

Parameters

NameInTypeRequiredDescription
namepathstringYesConnection name

Responses

CodeDescription
204Deleted
401No token to sign in to the secret store with
404No such connection
502The secret store did not answer
503The secret store is not configured

POST /api/connections/personal/{name}/break-glass​

Operation id connections_personal_break_glass: in Python client.call("connections_personal_break_glass", …), on the command line stardelt api connections_personal_break_glass.

Always refused: break-glass covers group connections only. An administrator gets a 403 and an audit row; nothing is read.

Parameters

NameInTypeRequiredDescription
namepathstringYesConnection name

Request body

FieldTypeRequiredDefaultDescriptionConstraints
reasonstringYes

Responses

CodeDescription
401No token to sign in to the secret store with
403Break-glass covers group connections only, or the caller is not an administrator
503The secret store is not configured

PUT /api/connections/personal/{name}/secret​

Operation id connections_personal_rotate: in Python client.call("connections_personal_rotate", …), on the command line stardelt api connections_personal_rotate.

Rotate some fields of one of the caller's own connections; the fields that are not sent keep their value.

Parameters

NameInTypeRequiredDescription
namepathstringYesConnection name

Request body

FieldTypeRequiredDefaultDescriptionConstraints
secretobject (map of string)YesThe fields to rotate; a managed connection sends every field.

Responses

CodeDescription
200The connection after the rotation, without values
400A sentence from the connection rules; never a value
401No token to sign in to the secret store with
404No such connection
502The secret store did not answer
503The secret store is not configured

/api/health​

GET /api/health​

Operation id health: in Python client.call("health", …), on the command line stardelt api health.

Always 200: the kubelet's readiness probe must not take Trino queries offline because the state store is down. The body says what is wrong: db is up, down or disabled.

The readiness probe. It answers 200 without sign-in whenever the process is up, so a state store that is down never takes Trino queries offline; db says what is wrong.

Responses

CodeDescription
200The process is up; db is the state store's state

/api/lineage​

GET /api/lineage​

Operation id lineage_graph: in Python client.call("lineage_graph", …), on the command line stardelt api lineage_graph.

Parameters

NameInTypeRequiredDescription
catalogquerystringNoCatalog
depthqueryintegerNoDepth 1-5, default 2
schemaquerystringNoSchema
tablequerystringNoTable

Responses

CodeDescription
200Lineage graph
403Not visible

GET /api/lineage/runs/{run_id}​

Operation id lineage_run_detail: in Python client.call("lineage_run_detail", …), on the command line stardelt api lineage_run_detail.

Parameters

NameInTypeRequiredDescription
run_idpathstringYesRun ID

Responses

CodeDescription
200Run detail
404Not found

/api/me​

GET /api/me​

Operation id me: in Python client.call("me", …), on the command line stardelt api me.

The caller: static user in dev mode, verified claims (never tokens) in oidc mode. The oidc shape is the documented OidcMe superset of this one.

Responses

CodeDescription
200Current user, auth mode, demo flag and warehouse

/api/notebooks​

GET /api/notebooks​

Operation id notebooks_list: in Python client.call("notebooks_list", …), on the command line stardelt api notebooks_list.

GET /api/notebooks: the latest version of every notebook in the caller's groups, newest first. Administrators see their own groups only.

The latest published version of every notebook in the caller's groups, newest first. Each row carries the group, the name, the latest objectVersion, its size, uploadedAt as an RFC 3339 timestamp in UTC (SeaweedFS's RFC 1123 LastModified normalised) and the publisher's uploadedBy display name. An administrator gets no wider view than their own groups. Unset NOVA_S3_* is 503; an S3 that is down or refuses is 502. Answers Cache-Control: no-store.

Responses

CodeDescription
200One row per notebook
401Not signed in
502The notebook store did not answer
503Notebook storage is not configured

PUT /api/notebooks/{group}/{name}​

Operation id notebooks_publish: in Python client.call("notebooks_publish", …), on the command line stardelt api notebooks_publish.

PUT /api/notebooks/{group}/{name}: validate, strip, store one immutable version.

Publish a notebook as a new immutable object version in the notebooks bucket. Needs a signed-in person whose groups include {group} (403 you are not a member of the group <g>; administrators are not exempt). {name} must match ^[A-Za-z0-9][A-Za-z0-9._ -]{0,127}\.ipynb$ and the body must be an nbformat 4 notebook of at most 5 MiB. Every code cell's outputs is emptied and its execution_count set to null, and the notebook's widget metadata is removed, before storing — no cell output reaches the object store. A cell of an nbformat 4.5 notebook without an id gets one (cell-<position>), so the stored notebook stays valid nbformat. sha256 is of the stored (stripped) bytes. parameters are the name = literal lines of the first cell tagged parameters, at most 32; defaults are text and are never evaluated. A 61st publish by one person in an hour is 429. Publishing bytes the newest version already holds stores nothing, uses no slot and writes no audit line: the answer names that version with unchanged: true. With ?dryRun=true the same checks run and nothing is stored, no slot is used and no audit line is written: the answer says unchanged: true with the newest version, or unchanged: false with an empty objectVersion when a publish would store a new one. Unset NOVA_S3_* is 503 notebook storage is not configured; an S3 that is down or refuses is 502. Each publish that stores a version writes one audit line with group, name, version and size, never content.

Parameters

NameInTypeRequiredDescription
dryRunquerybooleanNotrue: validate the notebook and say whether the newest version already holds these bytes, without storing a version, using a publish slot or writing an audit line.
grouppathstringYesOne of the caller's groups
namepathstringYesNotebook file name, ending in.ipynb

Responses

CodeDescription
200The stored version and its parsed parameters; unchanged: true when the newest version already held these bytes; an empty objectVersion only on a dry run that would store a new version
400Bad name or not an nbformat 4 notebook
401Not signed in
403Not a member of the group
413Larger than 5 MiB
429More than 60 publishes in an hour
502The notebook store did not answer
503Notebook storage is not configured

DELETE /api/notebooks/{group}/{name}​

Operation id notebooks_delete: in Python client.call("notebooks_delete", …), on the command line stardelt api notebooks_delete.

DELETE /api/notebooks/{group}/{name}: the notebook and every version of it.

Delete a published notebook and every version of it from the notebooks bucket; the bucket is versioned, so each version is removed by its id and the bytes are gone, not hidden. Needs a signed-in person whose groups include {group} (403 you are not a member of the group <g>; administrators are not exempt). A notebook with no version is 404 no such notebook. The answer carries the number of versions removed. A schedule that pinned a deleted version fails its next run. Unset NOVA_S3_* is 503 notebook storage is not configured; an S3 that is down or refuses is 502 (a retry removes what is left). Each delete writes one audit line with group, name and the number of versions, never content. Answers Cache-Control: no-store.

Parameters

NameInTypeRequiredDescription
grouppathstringYesOne of the caller's groups
namepathstringYesNotebook file name, ending in.ipynb

Responses

CodeDescription
200The notebook and the number of versions removed
400Bad name
401Not signed in
403Not a member of the group
404No such notebook
502The notebook store did not answer
503Notebook storage is not configured

GET /api/notebooks/{group}/{name}/parameters​

Operation id notebooks_parameters: in Python client.call("notebooks_parameters", …), on the command line stardelt api notebooks_parameters.

GET /api/notebooks/{group}/{name}/parameters: the parameters of one version, so the Schedule dialog can offer the form the notebook declares.

The parameters of one published version of a notebook, read from the stored bytes: the name = literal lines of the first cell tagged parameters, in order, at most 32, defaults as text and never evaluated. ?version=<id> names the immutable version; omitted, the latest is read and its id answered. Needs a member of {group} (403 you are not a member of the group <g>; administrators are not exempt). An unknown version is 404 no such notebook version. The answer carries parameters and the objectVersion it describes — a schedule pins that id. Unset NOVA_S3_* is 503 notebook storage is not configured; an S3 that is down or refuses is 502. Answers Cache-Control: no-store.

Parameters

NameInTypeRequiredDescription
grouppathstringYesOne of the caller's groups
namepathstringYesNotebook file name, ending in.ipynb
versionquerystringNo

Responses

CodeDescription
200The parameters and the version they belong to
400Bad name
401Not signed in
403Not a member of the group
404No such notebook version
502The notebook store did not answer
503Notebook storage is not configured

GET /api/notebooks/{group}/{name}/versions​

Operation id notebooks_versions: in Python client.call("notebooks_versions", …), on the command line stardelt api notebooks_versions.

GET /api/notebooks/{group}/{name}/versions: the versions of one notebook, newest first, at most 50.

The published versions of one notebook, newest first, at most 50. Needs a member of {group} (403 you are not a member of the group <g>; administrators are not exempt). Each row carries the immutable objectVersion, its size, uploadedAt as an RFC 3339 timestamp in UTC (SeaweedFS's RFC 1123 LastModified normalised), the publisher's uploadedBy and the sha256 of the stored bytes. A group with no such notebook answers an empty list. Unset NOVA_S3_* is 503; an S3 that is down or refuses is 502. Answers Cache-Control: no-store.

Parameters

NameInTypeRequiredDescription
grouppathstringYesOne of the caller's groups
namepathstringYesNotebook file name, ending in.ipynb

Responses

CodeDescription
200The versions, newest first
400Bad name
401Not signed in
403Not a member of the group
502The notebook store did not answer
503Notebook storage is not configured

/api/openapi.json​

GET /api/openapi.json​

Operation id openapi_document: in Python client.call("openapi_document", …), on the command line stardelt api openapi_document.

Serve the document. Deliberately outside sign-in (wired next to /api/health in main): it carries shapes, not data.

This document. It answers without sign-in and carries shapes, not data.

Responses

CodeDescription
200The OpenAPI 3.1 document

/api/pipelines​

GET /api/pipelines​

Operation id pipelines_list: in Python client.call("pipelines_list", …), on the command line stardelt api pipelines_list.

The pipelines the caller may see: their groups' pipelines, and every pipeline for an administrator. A pipeline without spec.ownerGroup is administrators-only. state is one of loading, paused, running, failed, succeeded, idle; loaded is false while Airflow has no DAG for a new pipeline yet. Answers Cache-Control: no-store.

Responses

CodeDescription
200The visible pipelines, sorted by name
401No token to sign in with
502The scheduler or the cluster did not answer
503No cluster connection, or the scheduler is not configured

POST /api/pipelines​

Operation id pipelines_create: in Python client.call("pipelines_create", …), on the command line stardelt api pipelines_create.

Schedule a statement or a published notebook: create a Pipeline for one of the caller's owner groups (runAs: pipeline-<group>), validated so the loader accepts it. A body gives exactly one of a non-empty sql and a notebook ({ name, objectVersion, params }, #237) — both or neither is 400 give a statement or a notebook. Refusals, in this order: a name outside 1 to 40 lower-case letters, digits and dashes starting with a letter 400; a group the caller is not a member of 403 (you are not a member of the group <g> — administrators included); a group without a ready ServicePrincipal 409 with the principals route's message; an empty, over-20,000-character or NUL-carrying statement 400; a parameter name, a parameter count or value, or a ${…} placeholder the loader cannot fill 400; a notebook name outside ^[A-Za-z0-9][A-Za-z0-9._ -]{0,127}\.ipynb$ 400, more than 32 notebook parameters, a value over 256 characters or NUL, or a params JSON text over 4096 characters 400, and a version that is not in the store 400 that notebook version does not exist in group <g> (503 when NOVA_S3_* is unset, 502 when S3 does not answer); a retry, wait, time-limit or run-mode option out of range 400; a cron that is not five in-range fields or fires more often than every five minutes 400; an unknown IANA timezone 400; a name that already exists 409; more than ten creations per user per hour 429. options and params are optional: an omitted option keeps its default (1 retry after 5 minutes, a 120-minute time limit, Forbid); params are the statement's ${params.<name>} values, a notebook's values are in notebook.params. Airflow is not needed: the resource is the source of truth, and the answer is the new pipeline with loaded: false, state: "loading". The audit line carries the statement's length, the cron and, for a notebook, the group, the file name and the pinned version — never the statement and never a parameter value. Answers Cache-Control: no-store.

Request body

FieldTypeRequiredDefaultDescriptionConstraints
namestringYes
notebookobjectNoA published notebook version to run instead of a statement.
notebook.namestringYes<group>/<name> is the object key; the group is the pipeline's ownerGroup, so a notebook of another group can never be named.
notebook.objectVersionstringYesThe S3 object version, never a branch or a tag: exactly the bytes a person published.
notebook.paramsobject (map of string)NoParameter values by name; at most 32, names [A-Za-z_][A-Za-z0-9_]{0,63}, values up to 256 characters. Written as the task's config.params JSON object string.
optionsobjectNoRetries, the wait between them, the time limit and the run mode; omitted fields keep the defaults.
options.backoffMinutesinteger (nullable)NoThe wait between attempts, 1 to 60 minutes.format int64
options.concurrencystring (nullable)NoForbid (one run at a time, the default) or Allow (runs side by side).
options.retriesinteger (nullable)NoExtra attempts after a failure, 0 to 5.format int64
options.timeoutMinutesinteger (nullable)NoThe longest a whole run may take, 1 to 720 minutes.format int64
ownerGroupstringYes
paramsobject (map of string)NoValues for ${params.<name>} in the statement; at most 16. A notebook's values are in notebook.params, not here.
provenanceobjectNoWhich bundle, target and commit this pipeline is deployed from (stardelt bundle deploy).
provenance.bundlestringYesThe bundle's name: 3 to 40 lower-case letters, digits and dashes.
provenance.revisionstring (nullable)NoThe commit the bundle was read at: 7 to 64 hex digits, with -dirty appended when the working tree had uncommitted changes. Omitted when the bundle is not in a Git checkout.
provenance.targetstringYesThe target of the bundle: 1 to 32 lower-case letters, digits and dashes.
scheduleobjectNoA cron expression and its IANA zone; both required when a schedule object is sent.
schedule.cronstringYes
schedule.timezonestringYes
sqlstringNoThe statement to run; exactly one of this and notebook is given.

Responses

CodeDescription
201The resource as written
400The name, the statement, the cron or the timezone is invalid
401No token to sign in with
403The caller is not a member of the owner group
409No ready principal for the group, or the name is taken
429More than ten creations in the last hour
502The cluster did not answer
503No cluster connection

POST /api/pipelines/check​

Operation id pipelines_check: in Python client.call("pipelines_check", …), on the command line stardelt api pipelines_check.

What the engine says about a statement, without running it: Nova fills the statement's ${run.date} (today, UTC), ${run.id} (check) and ${params.<name>} placeholders and sends EXPLAIN <statement> to the engine as the caller, under the source nova-schedule-check. It never sends EXPLAIN ANALYZE and never the statement itself. checked: false means this kind of statement cannot be checked here (only select, with, insert, merge, delete, update, values and create … as … are). A refused statement answers 200 with ok: false and the engine's sentence; an engine that does not answer is 502. Refusals before the engine: an empty, over-20,000-character or NUL-carrying statement 400, a parameter table or a placeholder as on create 400. Answers Cache-Control: no-store.

Request body

FieldTypeRequiredDefaultDescriptionConstraints
paramsobject (map of string)NoThe parameters the statement's ${params.<name>} placeholders are filled with.
sqlstringYes

Responses

CodeDescription
200The engine's verdict, or that this statement cannot be checked
400The statement, a parameter or a placeholder is invalid
401No token to sign in with
502The query engine is not reachable

POST /api/pipelines/preview-schedule​

Operation id pipelines_preview_schedule: in Python client.call("pipelines_preview_schedule", …), on the command line stardelt api pipelines_preview_schedule.

The next fire times of a cron in its zone, RFC 3339 with that zone's offset (for example 2026-10-21T03:00:00+02:00), so the Schedule dialog can show what a cadence means before it is saved. A field or a zone as on create (400 with the same sentences); count is 1 to 10, 5 when omitted. Like croniter, a day matches when both day fields are restricted and either matches, and when one is unrestricted and the other matches. A wall-clock time the zone skips at a DST jump is not a run; a time that happens twice is answered with its first occurrence. A cron that can never fire answers fewer runs (at most 366 days are searched), not an error. Answers Cache-Control: no-store.

Request body

FieldTypeRequiredDefaultDescriptionConstraints
countinteger (nullable)NoHow many runs to list, 1 to 10; 5 when omitted.min 0
cronstringYes
timezonestringYes

Responses

CodeDescription
200The next runs in the schedule's zone
400The cron, the timezone or the count is invalid
401No token to sign in with

GET /api/pipelines/principals​

Operation id pipelines_principals: in Python client.call("pipelines_principals", …), on the command line stardelt api pipelines_principals.

The caller's own groups and the pipeline principal of each. principal is pipeline-<group> when a ServicePrincipal of that name with spec.ownerGroup equal to the group exists; ready is its status.ready. message is null when it is ready, otherwise the Ready condition's message (or no status yet), and for a group without a principal the make sync-principal NAME=pipeline-<group> GROUP=<group> command that creates one. A group without a ready principal cannot schedule. Answers Cache-Control: no-store.

Responses

CodeDescription
200One entry per group of the caller, sorted by group
401No token to sign in with
502The cluster did not answer
503No cluster connection

POST /api/pipelines/validate​

Operation id pipelines_validate: in Python client.call("pipelines_validate", …), on the command line stardelt api pipelines_validate.

Check a pipeline document without writing it: the body is the body of pipelines_create (the document stardelt apply reads). It runs every rule a create runs (the name, the caller's own group, the group's ready principal, exactly one of a statement and a notebook with the rules of its kind, the options, the schedule) and then looks at the name: free means create; a pipeline Nova wrote that the caller may act on, in the same group, means update; a pipeline written outside Nova, someone else's, or in another group is a problem. A rule that refuses is an answer, ok: false with one sentence in errors, status 200; nothing is written and no creation slot is taken. An accepted document with a schedule lists its next five fire times. A cluster or notebook store that does not answer is 502 or 503 as on create. Answers Cache-Control: no-store.

Request body

FieldTypeRequiredDefaultDescriptionConstraints
namestringYes
notebookobjectNoA published notebook version to run instead of a statement.
notebook.namestringYes<group>/<name> is the object key; the group is the pipeline's ownerGroup, so a notebook of another group can never be named.
notebook.objectVersionstringYesThe S3 object version, never a branch or a tag: exactly the bytes a person published.
notebook.paramsobject (map of string)NoParameter values by name; at most 32, names [A-Za-z_][A-Za-z0-9_]{0,63}, values up to 256 characters. Written as the task's config.params JSON object string.
optionsobjectNoRetries, the wait between them, the time limit and the run mode; omitted fields keep the defaults.
options.backoffMinutesinteger (nullable)NoThe wait between attempts, 1 to 60 minutes.format int64
options.concurrencystring (nullable)NoForbid (one run at a time, the default) or Allow (runs side by side).
options.retriesinteger (nullable)NoExtra attempts after a failure, 0 to 5.format int64
options.timeoutMinutesinteger (nullable)NoThe longest a whole run may take, 1 to 720 minutes.format int64
ownerGroupstringYes
paramsobject (map of string)NoValues for ${params.<name>} in the statement; at most 16. A notebook's values are in notebook.params, not here.
provenanceobjectNoWhich bundle, target and commit this pipeline is deployed from (stardelt bundle deploy).
provenance.bundlestringYesThe bundle's name: 3 to 40 lower-case letters, digits and dashes.
provenance.revisionstring (nullable)NoThe commit the bundle was read at: 7 to 64 hex digits, with -dirty appended when the working tree had uncommitted changes. Omitted when the bundle is not in a Git checkout.
provenance.targetstringYesThe target of the bundle: 1 to 32 lower-case letters, digits and dashes.
scheduleobjectNoA cron expression and its IANA zone; both required when a schedule object is sent.
schedule.cronstringYes
schedule.timezonestringYes
sqlstringNoThe statement to run; exactly one of this and notebook is given.

Responses

CodeDescription
200The verdict: accepted (with the action) or the reason it is not
401No token to sign in with
502The cluster did not answer
503No cluster connection

GET /api/pipelines/{namespace}/{name}​

Operation id pipelines_get: in Python client.call("pipelines_get", …), on the command line stardelt api pipelines_get.

One pipeline: the list fields, the latest 20 runs, and the resource's params, retries and options (defaults filled in when a field is absent, so the edit dialog can prefill). A pipeline whose DAG does not exist yet answers 200 with loaded: false and state: "loading". A pipeline of another group answers 404. History of a deleted pipeline is not offered in this version: without the resource the answer is 404.

Parameters

NameInTypeRequiredDescription
namepathstringYesPipeline name
namespacepathstringYesThe Pipeline's namespace (Nova's own)

Responses

CodeDescription
200The pipeline, its latest runs, params, retries and options
401No token to sign in with
404No such pipeline for this caller
502The scheduler or the cluster did not answer
503No cluster connection, or the scheduler is not configured

PUT /api/pipelines/{namespace}/{name}​

Operation id pipelines_update: in Python client.call("pipelines_update", …), on the command line stardelt api pipelines_update.

Change a pipeline Nova created (app.kubernetes.io/managed-by: stardelt-nova) and nothing else. A statement pipeline's body carries sql; a notebook pipeline's carries notebook ({ name, objectVersion, params }), and an update never changes which of the two it is: sql on a notebook pipeline is 400 this pipeline runs a notebook, a notebook on a statement pipeline 400 this pipeline runs a statement, and both or neither 400 give a statement or a notebook. The body describes the whole editable state: an omitted schedule removes it, an omitted params removes the table, an omitted option keeps its default, and a notebook body's file name, version and parameters replace the task's config. Refusals: a pipeline the caller may not act on 404; a pipeline Nova did not write, or one whose task shape is not exactly one run task of kind sql or notebook 409; the statement, a parameter, a placeholder, a notebook name, a parameter value or count, or a version that is not in the store (503 without NOVA_S3_*, 502 when S3 does not answer), an option, the cron or the timezone as on create 400; the group's principal no longer ready 409 with the principals route's message; a concurrent change to the resource 409 the pipeline changed while you edited it: reload. A body may carry resourceVersion (the one pipelines_spec answered): when it is not the stored one, the answer is that same 409 and nothing is written; omitted, the update behaves as before. The audit line carries the statement's length, the cron and, for a notebook, the group, the file name and the pinned version — never the statement and never a parameter value. Answers Cache-Control: no-store.

Parameters

NameInTypeRequiredDescription
namepathstringYesPipeline name
namespacepathstringYesThe Pipeline's namespace (Nova's own)

Request body

FieldTypeRequiredDefaultDescriptionConstraints
notebookobjectNoThe notebook, its version and its parameters for a notebook pipeline; a statement pipeline refuses it.
notebook.namestringYes<group>/<name> is the object key; the group is the pipeline's ownerGroup, so a notebook of another group can never be named.
notebook.objectVersionstringYesThe S3 object version, never a branch or a tag: exactly the bytes a person published.
notebook.paramsobject (map of string)NoParameter values by name; at most 32, names [A-Za-z_][A-Za-z0-9_]{0,63}, values up to 256 characters. Written as the task's config.params JSON object string.
optionsobjectNoThe whole option set; omitted means the defaults.
options.backoffMinutesinteger (nullable)NoThe wait between attempts, 1 to 60 minutes.format int64
options.concurrencystring (nullable)NoForbid (one run at a time, the default) or Allow (runs side by side).
options.retriesinteger (nullable)NoExtra attempts after a failure, 0 to 5.format int64
options.timeoutMinutesinteger (nullable)NoThe longest a whole run may take, 1 to 720 minutes.format int64
paramsobject (map of string)NoThe whole parameter table; omitted means none.
provenanceobjectNoWhich bundle, target and commit this update deploys. Omitted leaves what the pipeline already says untouched; given, it replaces it.
provenance.bundlestringYesThe bundle's name: 3 to 40 lower-case letters, digits and dashes.
provenance.revisionstring (nullable)NoThe commit the bundle was read at: 7 to 64 hex digits, with -dirty appended when the working tree had uncommitted changes. Omitted when the bundle is not in a Git checkout.
provenance.targetstringYesThe target of the bundle: 1 to 32 lower-case letters, digits and dashes.
resourceVersionstring (nullable)NoThe resourceVersion the caller read (pipelines_spec). When it is given and is not the stored one, somebody changed the pipeline in between: 409 the pipeline changed while you edited it: reload, and nothing is written. Omitted keeps the old behaviour (the update then only fails if the resource changes while the update itself runs).
scheduleobjectNoOmitted means no schedule: the pipeline then runs on demand only.
schedule.cronstringYes
schedule.timezonestringYes
sqlstringNoThe statement of a statement pipeline; a notebook pipeline refuses it.

Responses

CodeDescription
200The resource as written
400The statement, the cron or the timezone is invalid
401No token to sign in with
404No such pipeline for this caller
409Managed outside Nova, an uneditable shape, no ready principal, or a conflict
502The cluster did not answer
503No cluster connection

DELETE /api/pipelines/{namespace}/{name}​

Operation id pipelines_delete: in Python client.call("pipelines_delete", …), on the command line stardelt api pipelines_delete.

Delete a pipeline Nova created. Only a pipeline the caller may act on and that carries app.kubernetes.io/managed-by: stardelt-nova may be deleted: a kubectl or GitOps pipeline answers 409 instead. Airflow is not called — the resource is the source of truth; the DAG leaves the scheduler's default list after about 90 seconds while its history stays readable. Answers 204 with Cache-Control: no-store; the attempt is audited.

Parameters

NameInTypeRequiredDescription
namepathstringYesPipeline name
namespacepathstringYesThe Pipeline's namespace (Nova's own)

Responses

CodeDescription
204Deleted (or already gone)
401No token to sign in with
404No such pipeline for this caller
409The pipeline is managed outside Nova
502The cluster did not answer
503No cluster connection

POST /api/pipelines/{namespace}/{name}/pause​

Operation id pipelines_pause: in Python client.call("pipelines_pause", …), on the command line stardelt api pipelines_pause.

Pause a pipeline. Idempotent: pausing a paused pipeline answers 200. A pipeline the scheduler has not loaded answers 409. Answers the fresh PipelineView — the DAG as Airflow returned it after the patch — and Cache-Control: no-store. The attempt is audited, including a refusal.

Parameters

NameInTypeRequiredDescription
namepathstringYesPipeline name
namespacepathstringYesThe Pipeline's namespace (Nova's own)

Responses

CodeDescription
200The pipeline, now paused
401No token to sign in with
404No such pipeline for this caller
409The scheduler has not loaded this pipeline yet
502The scheduler or the cluster did not answer
503No cluster connection, or the scheduler is not configured

POST /api/pipelines/{namespace}/{name}/resume​

Operation id pipelines_resume: in Python client.call("pipelines_resume", …), on the command line stardelt api pipelines_resume.

Resume a pipeline. Idempotent: resuming a running pipeline answers 200. A pipeline the scheduler has not loaded answers 409. Answers the fresh PipelineView — the DAG as Airflow returned it after the patch — and Cache-Control: no-store. The attempt is audited, including a refusal.

Parameters

NameInTypeRequiredDescription
namepathstringYesPipeline name
namespacepathstringYesThe Pipeline's namespace (Nova's own)

Responses

CodeDescription
200The pipeline, now unpaused
401No token to sign in with
404No such pipeline for this caller
409The scheduler has not loaded this pipeline yet
502The scheduler or the cluster did not answer
503No cluster connection, or the scheduler is not configured

POST /api/pipelines/{namespace}/{name}/runs​

Operation id pipelines_run_trigger: in Python client.call("pipelines_run_trigger", …), on the command line stardelt api pipelines_run_trigger.

Start a run now. Nova refuses instead of passing Airflow's silent behaviour on: a pipeline the scheduler has not loaded yet answers 409, a paused pipeline 409 (Airflow would create a run that waits queued until someone resumes), more than ten starts per user per minute 429, and a Forbid or Replace pipeline whose latest five runs include a queued or running one 409 (Allow skips that check). logicalDate is always now, UTC, whole seconds, so the run has tasks; the note is Started from Nova by <display name>. Every attempt, including a refusal, writes one audit line without note text. Answers Cache-Control: no-store.

Parameters

NameInTypeRequiredDescription
namepathstringYesPipeline name
namespacepathstringYesThe Pipeline's namespace (Nova's own)

Responses

CodeDescription
201The new run, queued
401No token to sign in with
404No such pipeline for this caller
409The pipeline is not loaded, is paused, or already has an active run
429More than ten runs were started in the last minute
502The scheduler or the cluster did not answer
503No cluster connection, or the scheduler is not configured

GET /api/pipelines/{namespace}/{name}/runs/{run_id}​

Operation id pipelines_run_get: in Python client.call("pipelines_run_get", …), on the command line stardelt api pipelines_run_get.

One run of a pipeline with its task instances. The run id is looked up through this pipeline's DAG id, so a run id of another pipeline answers 404. Answers Cache-Control: no-store.

Parameters

NameInTypeRequiredDescription
namepathstringYesPipeline name
namespacepathstringYesThe Pipeline's namespace (Nova's own)
run_idpathstringYesAirflow's run id, for example manual__2026-10-20T07:23:50.608876+00:00

Responses

CodeDescription
200The run and its tasks
401No token to sign in with
404No such pipeline for this caller, or no such run
502The scheduler or the cluster did not answer
503No cluster connection, or the scheduler is not configured

POST /api/pipelines/{namespace}/{name}/runs/{run_id}/stop​

Operation id pipelines_run_stop: in Python client.call("pipelines_run_stop", …), on the command line stardelt api pipelines_run_stop.

Stop a run: mark it failed. This is not a cancel — work already in flight can finish and commit its side effects, so the answer promises only that the run is marked failed. The run id is looked up through this pipeline's DAG id first: a run id of another pipeline answers 404 and is never patched. A run that is not queued or running answers 409 and is not patched. Answers the fresh RunView and Cache-Control: no-store. The attempt is audited, including a refusal.

Parameters

NameInTypeRequiredDescription
namepathstringYesPipeline name
namespacepathstringYesThe Pipeline's namespace (Nova's own)
run_idpathstringYesAirflow's run id

Responses

CodeDescription
200The run, now failed
401No token to sign in with
404No such pipeline for this caller, or no such run
409The run is not active
502The scheduler or the cluster did not answer
503No cluster connection, or the scheduler is not configured

GET /api/pipelines/{namespace}/{name}/runs/{run_id}/tasks/{task_id}/logs​

Operation id pipelines_run_task_logs: in Python client.call("pipelines_run_task_logs", …), on the command line stardelt api pipelines_run_task_logs.

One page of a task's log: at most 2000 lines and 512 KiB, ANSI escapes removed, the ::group:: source marker dropped (it names internal object-store paths and is never returned). try defaults to the task's last attempt; token is the nextToken of the previous answer. Every read writes one audit line with user, pipeline, run and task — never the content. Answers Cache-Control: no-store.

Parameters

NameInTypeRequiredDescription
namepathstringYesPipeline name
namespacepathstringYesThe Pipeline's namespace (Nova's own)
run_idpathstringYesAirflow's run id
task_idpathstringYesTask id
tokenquerystringNoContinuation token from a previous answer
tryqueryintegerNoAttempt number; defaults to the task's last try

Responses

CodeDescription
200The log page
400try is not a positive number
401No token to sign in with
404No such pipeline for this caller, no such run, or no such task
502The scheduler or the cluster did not answer
503No cluster connection, or the scheduler is not configured

GET /api/pipelines/{namespace}/{name}/spec​

Operation id pipelines_spec: in Python client.call("pipelines_spec", …), on the command line stardelt api pipelines_spec.

The editable document of one pipeline, in the shape of the body of pipelines_create: name, ownerGroup, a sql statement or a notebook (file name, pinned object version, parameters), a schedule (cron and timezone; a resource without a zone is UTC), the params table (left out when empty) and the options with their defaults filled in. It is built from the resource alone, so it answers while the scheduler is down. resourceVersion is the resource's version; managedBy is nova or other (kubectl or GitOps); editable is true only for a pipeline Nova wrote that has one run task of kind sql or notebook. A pipeline that is not editable is still readable: its document then leaves out what the form cannot show. The document never holds status, annotations, a connection's secret or a token. A pipeline of another group answers 404. Answers Cache-Control: no-store.

Parameters

NameInTypeRequiredDescription
namepathstringYesPipeline name
namespacepathstringYesThe Pipeline's namespace (Nova's own)

Responses

CodeDescription
200The document, its resource version, who manages it, and whether it is editable
401No token to sign in with
404No such pipeline for this caller
502The cluster did not answer
503No cluster connection

/api/query​

POST /api/query​

Operation id query_run: in Python client.call("query_run", …), on the command line stardelt api query_run.

Run one SQL statement as the caller and return the whole result. Synchronous: the call returns when the query ends. At most 50 000 rows or about 64 MiB of data are returned; past that truncated is true and the rows are a prefix.

Request body

FieldTypeRequiredDefaultDescriptionConstraints
sqlstringYesOne SQL statement, run with the caller's identity.

Responses

CodeDescription
200The columns, the rows and the engine's query id
400The statement is empty, was refused or failed in the engine
502The query engine is not reachable

/api/query-log​

GET /api/query-log​

Operation id query_log_list: in Python client.call("query_log_list", …), on the command line stardelt api query_log_list.

GET /api/query-log?scope=mine|team|all&state=&warehouse=&q=&from=&to=&limit=&cursor=

The statements run on the engine, newest first, a page at a time. A person sees their own; an administrator may ask for everyone's with scope=all. Times are epoch milliseconds.

Parameters

NameInTypeRequiredDescription
cursorquerystringNoThe nextCursor of the previous page
fromqueryintegerNoCreated at or after this time
limitqueryintegerNoPage size from 1 to 200; 50 by default
qquerystringNoText inside the statement, or an exact query id; at most 200 characters
scopequerystringNomine (default), team or all. all is for administrators; team stays empty until ownership is recorded
statequerystringNoQUEUED, FINISHED, FAILED or CANCELED, in any case
toqueryintegerNoCreated at or before this time
warehousequerystringNoOnly queries that ran on this warehouse

Responses

CodeDescription
200One page of the log
400A parameter is not valid; the message names it
403scope=all by somebody who is not an administrator
503The state store is not configured

GET /api/query-log/{id}​

Operation id query_log_detail: in Python client.call("query_log_detail", …), on the command line stardelt api query_log_detail.

GET /api/query-log/{id} — everything stored about one query the caller may see.

One query with its whole statement. Somebody else's query answers 404, exactly like a missing one; an administrator who opens another person's query is audited.

Parameters

NameInTypeRequiredDescription
idpathstringYesThe engine's query id

Responses

CodeDescription
200The stored query
404No such query, or it is not the caller's to see
503The state store is not configured

/api/saved-queries​

GET /api/saved-queries​

Operation id saved_queries_list: in Python client.call("saved_queries_list", …), on the command line stardelt api saved_queries_list.

GET /api/saved-queries — the caller's own, newest first.

The caller's own saved queries, most recently changed first. Times are epoch milliseconds.

Responses

CodeDescription
200The caller's saved queries
503The state store is not configured

POST /api/saved-queries​

Operation id saved_queries_create: in Python client.call("saved_queries_create", …), on the command line stardelt api saved_queries_create.

POST /api/saved-queries — 201 with the stored row.

Save a query for the caller. The title is at most 200 characters and the SQL at most 100 000.

Request body

FieldTypeRequiredDefaultDescriptionConstraints
sqlstringYes
titlestringYes
warehousestringNoThe warehouse the query was written for; free text, may be empty.

Responses

CodeDescription
201The stored row
400The title or the SQL is empty or too long
503The state store is not configured

DELETE /api/saved-queries/{id}​

Operation id saved_queries_remove: in Python client.call("saved_queries_remove", …), on the command line stardelt api saved_queries_remove.

DELETE /api/saved-queries/{id} — 204; 404 when the id is not the caller's.

Delete one of the caller's saved queries.

Parameters

NameInTypeRequiredDescription
idpathintegerYesThe saved query's id

Responses

CodeDescription
204Deleted
404No such saved query, or it is not the caller's
503The state store is not configured

/api/service-principals​

GET /api/service-principals​

Operation id service_principals_list: in Python client.call("service_principals_list", …), on the command line stardelt api service_principals_list.

The service principals the caller may see: every ServicePrincipal of their groups, and all of them for an administrator. purpose is the resource's spec.purpose (default pipeline); audiences are the resource's audiences; ready is status.ready; message is null when ready, otherwise the Ready condition's message, or no status yet. Credentials are never part of an answer. Answers Cache-Control: no-store.

Responses

CodeDescription
200The caller's principals, sorted by name
401No token to sign in with
502The cluster did not answer
503No cluster connection

/api/statements​

GET /api/statements​

Operation id statements_list: in Python client.call("statements_list", …), on the command line stardelt api statements_list.

The statements the caller holds (at most ten), in order of their ids: state, rows read, whether the rows were cut and the seconds since the last request that named each. It answers "which ones do I hold?" when a submit says the caller already holds ten. Only the caller's own are listed. A statement another request is advancing at that moment is listed as RUNNING with rowCount 0.

Responses

CodeDescription
200The caller's statements

POST /api/statements​

Operation id statements_submit: in Python client.call("statements_submit", …), on the command line stardelt api statements_submit.

Submit one SQL statement as the caller and get a handle to it. The call waits up to waitSeconds (0 to 30, default 5): when the statement ends in that time the answer is 200 and carries the first chunk of rows, otherwise it is 202 and the caller polls statements_get. A statement that fails in the engine is still 200 with state: FAILED and error.

Request body

FieldTypeRequiredDefaultDescriptionConstraints
maxRowsinteger (nullable)NoThe most rows to keep, 1 to 100 000 (the default and the cap). A longer result is cut here, the query is cancelled in the engine and the statement ends SUCCEEDED with truncated: true.min 0
sqlstringYesOne SQL statement, run with the caller's identity.
waitSecondsinteger (nullable)NoHow long the call waits for the statement to end before it answers 202; 0 to 30, default 5.min 0; format int64

Responses

CodeDescription
200The statement ended within the wait
202The statement is still running; poll it
400The statement is empty
429The caller already holds ten statements; read or cancel one
502The query engine is not reachable
503Not available in demo mode

GET /api/statements/{id}​

Operation id statements_get: in Python client.call("statements_get", …), on the command line stardelt api statements_get.

The state of one of the caller's statements. Each call reads the engine's next pages with the caller's current token, for up to waitSeconds (0 to 30, default 0); the rows are read with statements_chunk_get. A statement that nobody polls for the engine's client timeout (5 minutes by default) is cancelled by the engine.

Parameters

NameInTypeRequiredDescription
idpathstringYesThe statement id from statements_submit
waitSecondsqueryintegerNoWait up to this many seconds (0 to 30) for the statement to end

Responses

CodeDescription
200The statement
401The engine refused the caller's token
404No such statement of the caller's
502The query engine is not reachable

DELETE /api/statements/{id}​

Operation id statements_cancel: in Python client.call("statements_cancel", …), on the command line stardelt api statements_cancel.

Cancel one of the caller's statements in the engine and forget it. Idempotent: it answers 204 whether or not the statement exists or is the caller's (it cancels only the caller's own).

Parameters

NameInTypeRequiredDescription
idpathstringYesThe statement id

Responses

CodeDescription
204Cancelled, or nothing to cancel

GET /api/statements/{id}/chunks/{n}​

Operation id statements_chunk_get: in Python client.call("statements_chunk_get", …), on the command line stardelt api statements_chunk_get.

One chunk (1 000 rows, the last one fewer) of a statement's rows, counted from 0. A chunk exists once it is full or the statement has ended; the call does not wait. With Accept: text/csv the rows come as CSV (RFC 4180, lines end with CRLF) instead of JSON: chunk 0 starts with a row of the column names, the other chunks have none, so the chunks joined in order are one file; null is an empty cell and an array or an object is written as its JSON text.

Parameters

NameInTypeRequiredDescription
idpathstringYesThe statement id
npathintegerYesThe chunk number, from 0

Responses

CodeDescription
200The rows of the chunk
404No such statement of the caller's, or no such chunk (yet)

/api/streams​

GET /api/streams​

Operation id streams_list: in Python client.call("streams_list", …), on the command line stardelt api streams_list.

The calling user's streams: every StreamApp of their groups, and all of them for an administrator. phase is the operator's status.phase (Pending when it has not reported yet); lastError is the application's own terminal message when the phase is Failed; lastCheckpointAt is empty until the operator can read the checkpoint and the UI must show —. stopped is spec.stopped. attempts is how many times Spark has submitted the application and lastSubmittedAt when it last did; both are absent until the operator reports them. No connection credentials and no checkpoint prefix are part of an answer. Answers Cache-Control: no-store.

Responses

CodeDescription
200The caller's streams, sorted by namespace and name
401No token to sign in with
502The cluster did not answer
503No cluster connection

PATCH /api/streams/{namespace}/{name}​

Operation id streams_patch: in Python client.call("streams_patch", …), on the command line stardelt api streams_patch.

Stop or resume one stream: merge spec.stopped and answer the updated view.

Stop or resume one stream by setting spec.stopped (the one verb a stream has, D42 §4.4). true stops the job and keeps the checkpoint so false resumes from it. A stream the caller may not see, a foreign namespace, and a name that does not exist all answer the same 404. The answer is the updated stream view. Answers Cache-Control: no-store.

Parameters

NameInTypeRequiredDescription
namepathstringYesThe stream's name
namespacepathstringYesThe stream's namespace; only Nova's own namespace

Request body

FieldTypeRequiredDefaultDescriptionConstraints
stoppedbooleanYesThe desired spec.stopped.

Responses

CodeDescription
200The stream, with spec.stopped applied
401No token to sign in with
404No such stream, or the caller may not see it, or the namespace is not Nova's own
502The cluster did not answer
503No cluster connection

/api/warehouses​

GET /api/warehouses​

Operation id warehouses_list: in Python client.call("warehouses_list", …), on the command line stardelt api warehouses_list.

GET /api/warehouses: the compute classes of the platform, which one this caller may use and which one a query will land in.

Every SqlWarehouse of the platform namespace, largest class first (large, medium, small, xs; name order inside one size), each row carrying the class size, the numbers the resource group is rendered with (running, queued, memoryShare: the class defaults, overridden by spec.concurrency), default, canUse (one of the caller's groups is in spec.grants) and yours (the first usable row — the class the engine's first matching selector picks — or the default class when nothing is granted). An administrator gets one synthetic row first (name: "admin", displayName: "Administrators", size: null, 50/100/100 %: the admin root group access-sync renders) with yours on it and yours: false on every other row. Non-authoritative: the engine selects by identity, Nova only shows it. Without a cluster connection (demo mode, a local run) the answer is the one small class. A cluster that refuses or fails is a 502. Answers Cache-Control: no-store.

Responses

CodeDescription
200One row per class, largest first, the administrator's row first of all
401Not signed in
502The cluster did not answer, or Nova may not list sqlwarehouses