Reference
API reference
A REST API over HTTPS. JSON in, JSON out. Base URL https://api.polnor.net/api/v1. Every call is authenticated and scoped to a single workspace.
Conventions
Base URL & format
All endpoints live under https://api.polnor.net/api/v1. Request and response bodies are JSON; send Content-Type: application/json on writes. Timestamps are ISO-8601 UTC.
Pagination
List endpoints accept limit (default 50, max 200) and cursor. The response carries next_cursor when more rows exist.
GET /compute?limit=50&cursor=eyJpZCI6...
# → { "data": [ … ], "next_cursor": "eyJpZCI6…" } Rate limits & quotas
Quota ceilings return 429 with the exact limit in the body, for example max_running_runs=10, current=10.
Errors
| Status | Meaning |
|---|---|
400 | Malformed request or invalid parameters. |
401 | Missing or expired token. |
403 | Authenticated but not permitted. |
404 | Not found, or not in your workspace. |
409 | Conflict (for example, resource already in that state). |
429 | Quota exceeded. |
Errors share one shape: a JSON object with a single error string.
Authentication
Exchange credentials for a bearer token and send it on every call. See Authentication for API keys and agent tokens.
/auth/loginExchange email and password for a short-lived access token.
Request
curl -s https://api.polnor.net/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"you@acme.eu","password":"••••••"}' 200 OK
{
"access_token": "eyJhbGciOi…",
"token_type": "Bearer",
"expires_in": 3600
} /auth/refreshIssue a fresh access token before the current one expires.
Compute
GPU and CPU instances provisioned on your own cloud. See the Compute guide.
/computeList compute instances in the workspace.
200 OK
{ "data": [
{ "id": "cmp_8f21", "name": "training-a100",
"flavor": "gpu.a100", "status": "running",
"region": "GRA9", "auto_stop": "2h" }
] } /computeCreate an instance. It is provisioned on the cloud provider registered for the workspace.
Body
| Field | Type | Notes |
|---|---|---|
name | string | Required. Unique in the workspace. |
flavor | string | Required. e.g. gpu.a100, cpu.32. |
auto_stop | string | 30m, 2h or never. Default 1h. |
image | string | Optional. Pinned on first start. |
curl -s https://api.polnor.net/api/v1/compute \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"training-a100","flavor":"gpu.a100","auto_stop":"2h"}' 201 Created
{ "id": "cmp_8f21", "status": "stopped" } /compute/:id/startStart a stopped instance. The image is persisted on first start for reproducible restarts.
/compute/:id/stopStop a running instance and release the underlying VM.
/compute/:idDelete an instance. Returns 409 if it is still running.
SQL warehouses
On-demand DuckDB + Spark warehouses. See the SQL warehouses guide.
/sql/warehousesList warehouses and their state (stopped, starting, running, stopping).
/sql/warehouses/:id/startProvision the warehouse VM with DuckDB and Spark sidecars. Takes roughly three minutes to reach running.
/sql/executeRun a query. Reads route to DuckDB, writes to Spark, automatically.
Request
curl -s https://api.polnor.net/api/v1/sql/execute \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"query":"SELECT count(*) FROM acme.sales.orders",
"warehouse_id":"wh_4a2b"}' 200 OK
{
"columns": ["count"],
"rows": [[1284902]],
"engine": "duckdb",
"elapsed_ms": 412,
"lineage": { "read": ["acme.sales.orders"] }
} Catalog
Iceberg namespaces and tables, via the Lakekeeper catalog. See the Lakehouse guide.
/databasesList databases (Iceberg namespaces) in the workspace.
/databases/:id/tablesList tables in a namespace, with row counts and current snapshot.
/databases/:id/schemas/:schema/tables/:table/schemaReturn a table's column schema (name and Iceberg type).
{ "columns": [
{ "name": "id", "type": "long" },
{ "name": "amount", "type": "decimal(10,2)" }
] } Jobs
Jobs run a command or a multi-task DAG. See the Jobs guide.
/jobsCreate a job. Pass tasks to declare a DAG, or omit it for a single command.
{
"name": "daily-pipeline",
"tasks": [
{ "key": "ingest", "command": "python ingest.py" },
{ "key": "transform", "command": "python tx.py",
"depends_on": ["ingest"], "max_retries": 2, "timeout_seconds": 1800 }
]
} /jobs/:idUpdate a job. tasks: null leaves the DAG intact; tasks: [] clears it. Editing never rewrites past runs.
/jobs/:id/runTrigger a run. Subject to the max_running_runs quota.
{ "run_id": "run_4812", "status": "pending" } Runs & tasks
/runs/:idRun status and timing. Statuses: pending, running, success, failed, cancelled.
/runs/:id/tasksPer-task rows for a run (empty for single-command jobs).
[ { "key": "ingest", "status": "success", "retry_count": 0 },
{ "key": "transform", "status": "running" } ] /runs/:id/tasks/:key/logsLogs for a single task, streamed and archived.
/runs/:id/lineageTables this run read and wrote, captured from each query. See Governance.
/runs/:id/cancelCancel a run. Pending and running tasks are cancelled; the agent SIGTERMs then SIGKILLs live processes.
Experiments & models
MLflow-compatible tracking. See Models & serving and the Python SDK.
/experiments/:id/runs/:run/metricsLog a metric. Stored as the latest value and as a step history.
{ "key": "auc", "value": 0.934, "step": 4200 } /experiments/:id/runs/:run/metrics/batchLog many metrics in one call.
/experiments/:id/runs/:run/metrics/:keyFull step / value / timestamp history for a metric.
/experiments/:id/runs/:run/artifacts/presigned-urlGet a presigned URL to upload an artifact straight to your own S3.
/models/:id/endpointsServing endpoints backed by a model version.
Serving endpoints
/endpointsList serving endpoints with status, model version, QPS and latency.
/endpointsCreate an endpoint from a model version. Subject to the max_endpoints quota.
{ "name": "churn-prod", "model": "churn-v3", "version": 7 } Audit & lineage
/audit/analyticsDaily roll-ups of the audit log.
GET /audit/analytics?from=2026-06-01&to=2026-06-12&group_by=day,action Billing
See the Billing guide and Pricing.
/settings/billing/invoicesList the current workspace's invoices (draft, sent, paid).
/settings/billing/usage?from=&to=Usage and cost for a date range: provider cost, multiplier, and Polnor margin.
{ "data": [
{ "category": "compute_gpu", "provider_cost": 1842.50,
"multiplier": 2.0, "total_cost": 1842.50 }
] }