Actions — API Reference
Package: dodil.data.actions.v1 · Service: ActionsService · Proto: k3_actions.proto
This is a control-plane service on k3-api. HTTP is https://api.data.dodil.io; gRPC is rpc.data.dodil.io:443. Auth is a bearer JWT in Authorization — see Conventions. Every RPC below authorizes against the selection’s (or request’s) bucket.
| RPC | HTTP | Streaming |
|---|---|---|
ListAvailableActions | POST /actions/list | — |
RunAction | POST /actions/run | NDJSON (application/x-ndjson) |
EnqueueAction | POST /actions/enqueue | — |
EnqueueBulkActions | POST /actions/enqueue/bulk | — |
WatchActionRun | POST /actions/runs/watch | NDJSON (application/x-ndjson) |
ListActiveRuns | POST /actions/runs/active | — |
ListRecentActions | POST /actions/runs/list | — |
GetActionRun | POST /actions/runs/get | — |
SaveActionOutput | POST /actions/runs/save | — |
ChatTurnis gone. The conversational-agent RPC and its whole message set were declared, deferred, and removed before any implementation landed — both halves returnedUNIMPLEMENTED. Do not build against a chat endpoint; the action menu (ListAvailableActions+RunAction) is the live surface.
gRPC setup —
grpcurl, endpoints, reflection, and field-name casing — is covered once in Conventions → Using gRPC. Request bodies use proto field names (snake_case); HTTP is camelCase both ways.
The selection model
Every dispatch carries a Selection. Its oneof shape is what K3 maps to the action’s inputs, and its pillar string ("objects" | "vectors" | "tables" | "") filters the catalog.
message Selection {
string pillar = 1; // "objects" | "vectors" | "tables" | "" (agentic)
oneof shape {
ObjectSelection object_single = 10; // { bucket, key, content_type? }
ObjectsSelection objects_multi = 11; // { bucket, keys[], content_types[]? }
CollectionSelection collection_single = 12; // { bucket, collection }
ChunksSelection chunks_multi = 13; // { bucket, collection, chunk_ids[] }
TableSelection table_single = 14; // { bucket, table_name }
NoSelection none = 15; // { bucket } — agentic
}
}content_type is optional on object selections — K3 will HEAD the object server-side if you omit it. The UI carries it so it can pre-filter the menu before you click.
ListAvailableActions
Filters the Scriptum action catalog against your current selection and returns the actions that fit, with each one’s render-time form (params_form) and content-type accept-lists. Server-side it is a Scriptum ListTemplates proxy plus a K3-side filter.
HTTP
POST /actions/list
{
"selection": {
"pillar": "objects",
"objectSingle": { "bucket": "kb-prod", "key": "contracts/acme.pdf" }
},
"search": ""
}Response — one AvailableAction per fitting action:
{
"actions": [
{
"actionId": "summarization",
"name": "summarization",
"description": "Multi-level document summarization with keyword extraction.",
"icon": "file-text",
"visibility": "primary",
"invoke": "sync",
"paramsForm": { "inputs": [ … ], "enums": [ … ], "outputs": [ … ] },
"acceptedContentTypes": ["application/pdf", "text/plain", … ],
"acceptedExtensions": ["pdf", "txt", "md", … ]
}
]
}params_form is a ScriptContract — the same type the pipeline pillar exposes for template forms, so the K3-bound inputs (url, object, bucket, keys, collection, …) are stripped out; what remains is what a UI renders as the per-action form (question, target_language, llm_model, max_tokens, …).
RunAction
One-shot streaming dispatch — the right-click → pick UX. K3 resolves the selection into action inputs, injects service-account credentials, calls Scriptum, and translates thread events into ActionEvents.
HTTP transport is NDJSON: the response body is one JSON-encoded ActionEvent per line (Content-Type: application/x-ndjson), no envelope and no trailing comma. Consume it with fetch() + a ReadableStream + line-buffered JSON.parse. SSE is deliberately not used — EventSource cannot carry the bearer token. A mid-stream error is folded into a final NDJSON line, so the terminator is always structured.
HTTP
POST /actions/run // → application/x-ndjson
{
"selection": {
"pillar": "objects",
"objectSingle": { "bucket": "kb-prod", "key": "contracts/acme.pdf" }
},
"actionId": "summarization",
"params": { "style": "bullets" }
}Response body, one event per line:
{"started":{"actionId":"summarization","threadId":"th_…","runId":"run_…","startedAtMs":"1724930000000"}}
{"progress":{"stepPath":"1.0","stepName":"map","value":{ … }}}
{"completed":{"output":{"summary":"…","keywords":["…"]},"durationMs":"8200"}}A failure terminates the stream with a failed line instead:
{"failed":{"error":"…","failedStepPath":"1.0","failedStepName":"map"}}For sync actions the stream is started → completed with no progress yields. output is the action’s final output_json, rendered against the template’s declared outputs (the same ScriptContract).
EnqueueAction
The queued twin of RunAction: same request shape, but instead of streaming it publishes the dispatch onto the K3_ACTIONS work queue and returns the allocated run_id at once. K3-side adaptation errors (missing required field, bad selection, Scriptum down) surface synchronously here; dispatch-time errors land in the manifest as FAILED and surface via ListRecentActions.
HTTP
POST /actions/enqueue
{
"selection": { "pillar": "objects",
"objectSingle": { "bucket": "kb-prod", "key": "contracts/acme.pdf" } },
"actionId": "translation",
"params": { "target_language": "fr" },
"batchId": ""
}{ "runId": "run_…", "reverseTs": "…", "batchId": "" }EnqueueBulkActions
Fan one action across N selections in a single round-trip — the “100 files × 1 translation” case becomes one request returning 100 run_ids. Partial failure is per-selection: run_ids[i] and errors[i] are aligned arrays, so a bad selection fails only its own slot. A top-level error means nothing was enqueued.
HTTP
POST /actions/enqueue/bulk
{
"selections": [
{ "pillar": "objects", "objectSingle": { "bucket": "kb-prod", "key": "docs/a.pdf" } },
{ "pillar": "objects", "objectSingle": { "bucket": "kb-prod", "key": "docs/b.pdf" } }
],
"actionId": "translation",
"params": { "target_language": "fr" }
}{ "batchId": "…", "runIds": ["run_a", "run_b"], "errors": ["", ""] }WatchActionRun
Stream the live ActionEvents for a single queued (or streaming) run — same NDJSON encoding as RunAction, so one parser serves both. Closing the stream client-side does not cancel the dispatch; the run is queue-owned. If the run has already terminated, the stream falls back to the manifest.
HTTP
POST /actions/runs/watch // → application/x-ndjson
{ "runId": "run_…", "bucket": "kb-prod" }ListActiveRuns
The in-flight queue snapshot for a bucket — a refresh-proof, cross-tab view of what is running right now. Pairs with ListRecentActions for terminal history.
HTTP
POST /actions/runs/active
{ "bucket": "kb-prod" }{ "runs": [ { "runId": "run_…", "actionId": "translation",
"selectionLabel": "a.pdf", "batchId": "…",
"phase": "ACTIVE_RUN_PHASE_RUNNING",
"enqueuedAtMs": "…", "startedAtMs": "…", "threadId": "th_…" } ] }Run history — ListRecentActions, GetActionRun, SaveActionOutput
Every terminal dispatch writes a manifest at _actions/runs/{reverse_ts}_{run_id}/manifest.json. Listing is one S3 LIST (reverse-timestamp order → newest first) plus N parallel manifest GETs — no sidecar index. status / action_id filters are applied after the fetch.
HTTP
POST /actions/runs/list
{ "bucket": "kb-prod", "limit": 20 }POST /actions/runs/get
{ "bucket": "kb-prod", "runId": "run_…", "inlineSibling": false }POST /actions/runs/save
{ "bucket": "kb-prod", "runId": "run_…",
"s3": { "targetBucket": "kb-prod", "targetKey": "reports/acme-summary.md" } }An ActionRunManifest records status (ACTION_RUN_STATUS_{RUNNING,COMPLETED,FAILED,CANCELLED}), timing, the selection and params it ran with, an output_kind renderer hint (JSON / TEXT / MARKDOWN / TABLE_ROWS / FILE / CHUNKS / NONE), and where the output lives:
- inline — small payloads (
output_inline, ≤ ~16 KB) sit in the manifest itself. - sibling file — medium payloads at
_actions/runs/{reverse_ts}_{run_id}/output.{ext}. - self-routed — templates that write to a warehouse table (
WarehouseRowsRef) or an S3 key (S3ObjectRef) at run time, or vector chunks (VectorChunksRef) forchunks-kind actions.
Temp runs under _actions/runs/ carry a TTL via S3 lifecycle. SaveActionOutput copies the output to a permanent S3 key or appends its rows to a warehouse table (merge uses the table’s merge keys) and stamps the manifest’s promoted field — object content is unchanged, and the call is idempotent on (run_id, target_key).
See also
- Actions — Overview — the selection model and where actions sit in the three-service split
- CLI Guide —
dodil data actions list·run - Catalog — the shipping actions and their selection kinds
- Conventions — auth headers, error envelope, using gRPC
- Pipelines — API Reference — the automatic side (rules run templates on upload)