Skip to content

Worked HTTP workflows

Role: Client integrator implementing granular HTTP operations. Prerequisites: An authenticated CLI profile, jq, and access to the selected model; curl and a scoped personal token for the raw HTTP examples. Files: Saved model discovery, request body, operation key, receipt and optional private HTTP headers.

For ordinary CLI executions and scripts, use the analysis-directory workflow and noninteractive JSON recipe. This page documents integration contracts and the raw API escape hatch. The HTTP reference defines each endpoint’s complete request, response, status and permission contract. These examples show how to combine those endpoints. Use your deployed service’s /api/v1/openapi.json when its version differs from this documentation.

enumeray api handles profile credentials and structured errors while exposing methods and paths directly. Its --file is a JSON request body; it does not upload the contents of files mentioned inside that JSON. Paths must begin with /api/v1/ and include any query string:

Terminal window
enumeray --json api GET /api/v1/me
enumeray --json api GET /api/v1/models
enumeray --json api GET '/api/v1/runs?model_id=example&status=succeeded&limit=100'

Analysis directories validate scientific inputs, retain reviewed publication/disclosure, bind the API/account and manage upload/submission recovery. Independent artifact commands manage reusable transfers. The raw api escape hatch does not add those workflow checks; integrations must preserve their own exact request, identity and retry evidence.

Discover, save and submit one JSON operation

Section titled “Discover, save and submit one JSON operation”

Start in a new private directory. This example assumes the scalar fixture’s value input. Review the selected model’s real contract and disclosure before submitting.

Terminal window
umask 077
mkdir http-run
cd http-run
MODEL_ID=example
enumeray --json api GET "/api/v1/models/$MODEL_ID" > model.json
jq '.current_publication | {definition, execution_location, disclosure}' model.json
PUBLICATION_ID=$(jq -er '.current_publication_id' model.json)
jq -n --arg model "$MODEL_ID" --arg publication "$PUBLICATION_ID" \
'{model_id: $model, expected_publication_id: $publication, inputs: {value: 2}}' \
> request.json
python3 -c 'import uuid; print(uuid.uuid4())' > operation-key.txt

request.json is a complete HTTP body:

{
"model_id": "example",
"expected_publication_id": "release-one",
"inputs": {"value": 2}
}

The expected publication prevents silently running a newer contract than you reviewed. The operation key identifies the intended run, independently of the HTTP request ID. Keep both files unchanged after the first submission:

Terminal window
OPERATION_KEY=$(cat operation-key.txt)
enumeray --json api POST /api/v1/runs --file request.json \
--idempotency-key "$OPERATION_KEY" > receipt.json
RUN_ID=$(jq -er '.run_id' receipt.json)
enumeray --json api GET "/api/v1/runs/$RUN_ID" > run.json
jq '{state, phase, cancellation, result}' run.json

Repeat the same POST with the saved key and body after a timeout or lost reply. Both first acceptance and exact replay return HTTP 201 with the original receipt and a Location pointing to the run. The receipt’s state remains queued; GET the run for current state. A changed request with the same key is a conflict. Unlike analysis-directory submission, api does not verify that saved files belong to the same account, so your integration must preserve that identity too.

Create a personal token while signed in with your regular account. The example scopes permit discovery, submission, reading, cancellation and identity verification:

Terminal window
enumeray auth token-create http-client \
--scopes me:read,models:read,runs:create,runs:read,runs:cancel \
--expires-in-seconds 86400 --output token.json
umask 077
jq -er '"Authorization: Bearer " + .token' token.json > auth-header.txt
API_URL=https://enumeray.example.org
curl --fail-with-body --silent --show-error --header @auth-header.txt \
"$API_URL/api/v1/me"

Use your real API origin. The header file keeps the bearer value out of process arguments. It remains a credential: keep it private along with token.json. Token scopes do not replace account/model access. Refresh this header file after issuing a replacement token; personal tokens do not use the browser refresh flow.

To submit the saved JSON request with curl, first persist a header file containing the same operation key:

Terminal window
{ cat auth-header.txt; printf 'Idempotency-Key: %s\n' "$(cat operation-key.txt)"; } \
> submission-headers.txt
curl --fail-with-body --silent --show-error \
--header @submission-headers.txt --header 'Content-Type: application/json' \
--data-binary @request.json --dump-header response-headers.txt \
"$API_URL/api/v1/runs" > curl-receipt.json

Inspect the HTTP status and JSON body. --fail-with-body returns nonzero on HTTP errors while retaining the error response. Do not automatically retry validation, authorization or generation conflicts. Keep x-request-id from response headers for diagnosis; it changes on every request and cannot replace the operation key.

Submit small files in one multipart request

Section titled “Submit small files in one multipart request”

For clients that already implement HTTP, the run endpoint also accepts bounded multipart/form-data. The normal CLI file workflow binds sources with analysis init --file SLOT=PATH and manages verified uploads automatically; see file inputs. Multipart is convenient for small attachments and requires configured versioned storage. It accepts at most eight attachments and is also subject to the service’s general request-body limit.

For the file starter, use new operation-specific files in the same private directory as auth-header.txt above, and discover the model’s current publication. Save a JSON part that binds the scientific dataset slot to the multipart part named data:

Terminal window
MODEL_ID=csv-example
enumeray --json model get "$MODEL_ID" > file-model.json
PUBLICATION_ID=$(jq -er '.current_publication_id' file-model.json)
jq -n --arg model "$MODEL_ID" --arg publication "$PUBLICATION_ID" \
'{model_id: $model, expected_publication_id: $publication,
inputs: {dataset: {attachment: "data"}}}' > multipart-request.json
cat > dataset.csv <<'CSV'
index,value
0,2
1,3
CSV
python3 -c 'import uuid; print(uuid.uuid4())' > multipart-key.txt
{ cat auth-header.txt; printf 'Idempotency-Key: %s\n' "$(cat multipart-key.txt)"; } \
> multipart-headers.txt
curl --fail-with-body --silent --show-error --header @multipart-headers.txt \
--form 'request=@multipart-request.json;type=application/json' \
--form 'data=@dataset.csv;type=text/csv' \
"$API_URL/api/v1/runs" > multipart-receipt.json
RUN_ID=$(jq -er '.run_id' multipart-receipt.json)

Keep the JSON part, part names, media type, file bytes and key unchanged on exact retry. The server verifies and publishes the attachment as an artifact at acceptance; run inspection retains requested attachment inputs and effective artifact references. attachment belongs to this multipart envelope. Ordinary JSON submission uses artifact_id for an already-completed upload.

Let curl set the multipart boundary. The request part must have application/json, and a file part’s media type must match the scientific declaration. Do not send an author publication or a wrapper protocol request as a run submission.

Use the RUN_ID from the receipt and the private auth-header.txt above:

Terminal window
curl --no-buffer --fail-with-body --silent --show-error \
--header @auth-header.txt --header 'Accept: text/event-stream' \
--dump-header stream-headers.txt \
"$API_URL/api/v1/runs/$RUN_ID/logs/stream"

A log event has an opaque id, event: log and a JSON data payload containing a log entry. A client must durably retain the ID only after processing the entry. Reconnect after that ID using either Last-Event-ID or ?cursor=..., never both. For example, to follow from the end of a drained retained page:

Terminal window
enumeray --json run logs "$RUN_ID" > logs.json
# Follow next_cursor pages first if present; use the final page here.
RESUME_CURSOR=$(jq -er '.resume_cursor' logs.json)
ENCODED_CURSOR=$(jq -rn --arg value "$RESUME_CURSOR" '$value|@uri')
curl --no-buffer --fail-with-body --silent --show-error \
--header @auth-header.txt --dump-header stream-headers.txt \
"$API_URL/api/v1/runs/$RUN_ID/logs/stream?cursor=$ENCODED_CURSOR"

An HTTP 410 response means the cursor precedes retained history. Use X-History-Recovery-Cursor from the response headers and explicitly report the omitted prefix. During an open stream, handle retention_gap similarly; auth_revoked, source_unavailable and shutdown are control events that close the response. Keepalive comments are not log entries. Connection closure and silence do not establish scientific completion: independently GET the run and drain the retained final logs.

Execution-context manifests still send their spec to the matching HTTP endpoint. Editable author publication.json uses a different local format. Prefer model release init and resume for durable candidate registration from that source.

A direct HTTP integrator persists a machine request with one chosen publication ID before sending. The registration body remains PublicationCreate:

{
"id": "client-candidate-r1",
"context_id": "lab-cpu",
"descriptor": {
"release_path": "/opt/enumeray/models/example/r1",
"entrypoint": ["wrapper.py", "execute"],
"environment": {}
},
"definition": {
"contract_version": 1,
"model_identity": "example@v1",
"wrapper_identity": "example-wrapper@v1",
"runtime_identity": "author-example:python3-standard-library:1",
"wrapper_protocol_version": 1,
"dataset_identities": [],
"input_schema": {
"type": "object", "properties": {"value": {"type": "number"}},
"required": ["value"], "additionalProperties": false
},
"output_schema": {
"type": "object", "properties": {"doubled": {"type": "number"}},
"required": ["doubled"], "additionalProperties": false
},
"examples": [{"name": "double-two", "inputs": {"value": 2}, "outputs": {"doubled": 4}}]
}
}

Save it as publication-request.json, then use the administrator’s own active context grant:

Terminal window
enumeray api POST /api/v1/models/example/publications --file publication-request.json

After an uncertain response, inspect that exact candidate ID and retry the same saved body/account. A new ID expresses a new candidate. This registers without activating; connector readiness, scientific trials/review and guarded explicit activation remain necessary. Author files and saved machine requests are not interchangeable. For guarded management changes, inspect the current generation immediately before saving the mutation body:

Terminal window
enumeray --json access policy example > policy.json
GENERATION=$(jq -er '.generation' policy.json)
jq -n --argjson generation "$GENERATION" \
'{mode: "request", visibility: "listed", expected_generation: $generation}' \
> policy-change.json
enumeray api PUT /api/v1/models/example/access-policy --file policy-change.json

Omitted or null visibility preserves the stored visibility; an explicit listed/unlisted value changes it independently of access mode. A generation conflict needs another inspection and a deliberate decision about the intervening change. An exact replay after a successful policy update also conflicts with the original generation and makes no further change. Use the command reference to understand api options and the endpoint reference for each management body.

POST /api/v1/runs accepts purpose: "candidate_trial" in either the JSON body or the multipart request part. Keep model_id, expected_publication_id and inputs pinned to the candidate. Omitted purpose means standard, retaining the ordinary current-publication/admission checks. Trial requests require runs:create, models:manage, current model administration/execution access and ownership of the candidate’s captured grant or an exact active publication_delegation_id issued by its holder. Delegated trial requests must include that ID; it is retained through claim, launch control and provenance.

Trials can use ready or published retained installations. They bypass public admission without changing it. Fresh trials reject retiring/unavailable installations; accepted work retains its exact identity through activation, replacement and delivery. The same idempotency key cannot switch between standard and trial purpose. Successful retries return the original receipt after authority or publication changes, using an active credential with the required scopes.

Administrator publication detail exposes trial_availability for the caller and trial_evidence with counts, latest trial and latest successful trial references. Use ordinary run inspection, logs, artifacts and provenance to assess results. Readiness and execution success do not certify scientific correctness or activate a candidate. Follow the author trial recipe.

Documentation 205dcb3116ea · public sources 8c70356d340f · built .Download documentation identity.