Nushell: submit a Sume job and poll it to a result, no jq needed

A Nushell script that POSTs a Sume image job with an Idempotency-Key, polls /v1/jobs/{id}/status by next_poll_after_seconds, then fetches the result.

5 min readSume
All posts

In Nushell, submit a Sume job with http post --headers and mode: "async", keep the job id from the first response, then loop on GET /v1/jobs/{id}/status until terminal is true, sleeping for next_poll_after_seconds between reads, and fetch /result only when sume_status is completed. Because Nushell returns parsed records, there is no jq: you read $s.terminal directly. Send an Idempotency-Key on the submit so that a retried script returns the original job instead of billing a second one.

The loop follows Sume's Jobs and results page (read 2026-10-11), which calls it the client-side equivalent of subscribe().

Which fields should the loop read?

Sume jobs have five statuses: queued, processing, completed, failed, canceled. The last three are terminal. The docs recommend polling on the booleans terminal and result_ready or on sume_status, and warn against mixing them with the queue-shaped status field (IN_QUEUE, IN_PROGRESS, COMPLETED, ...), which maps one to one onto sume_status.

GET /v1/jobs/{id}/result answers 409 job_not_completed unless the job completed, so a failed job needs its error from the job record (GET /v1/jobs/{id}), not from /result.

Job statuses and the poll-loop actions from Sume's Jobs and results page, read 2026-10-11.
sume_statusTerminalWhat the script does
queuedNoSleep, then poll again
processingNoSleep, then poll again
completedYesGET /v1/jobs/{id}/result
failedYesRead the error from GET /v1/jobs/{id}
canceledYesStop; nothing to fetch

What is the script?

It stops with an error if the key is missing, derives the idempotency key from the prompt hash, and gives up after ten minutes with a message that the job keeps running and billing. The response handling accepts either a bare record or one wrapped in data.

# nu poll.nu "a matte black bottle on marble"
def main [prompt: string] {
  let key = ($env.SUME_API_KEY? | default "")
  if ($key | is-empty) { error make {msg: "set SUME_API_KEY"} }
  let base = ($env.SUME_API_BASE? | default "https://api.sume.com")
  let h = {Authorization: $"Bearer ($key)", Idempotency-Key: $"nu-(($prompt | hash sha256) | str substring 0..15)"}
  let sub = (http post --content-type application/json --headers $h $"($base)/v1/image-1.0/generate" {prompt: $prompt, mode: "async"})
  let id = ($sub.request_id? | default $sub.id?)
  mut s = {terminal: false}
  mut waited = 0sec
  while (not $s.terminal) and $waited < 10min {
    let r = (http get --headers $h $"($base)/v1/jobs/($id)/status")
    $s = ($r.data? | default $r)
    if $s.terminal { break }
    let pause = (($s.next_poll_after_seconds? | default 3) * 1sec)
    sleep $pause
    $waited = $waited + $pause
  }
  if not $s.terminal { error make {msg: $"still running, job ($id) keeps billing"} }
  if $s.sume_status != "completed" { error make {msg: $"job ($id) ended ($s.sume_status)"} }
  http get --headers $h $"($base)/v1/jobs/($id)/result"
}

What did I actually run?

I ran this on Nushell 0.116 against a small local stub that answers the submit with 202 and a request_id, answers the status twice as processing with next_poll_after_seconds: 1, then as completed, and returns an artifact list from /result. It sent one Idempotency-Key, made three status reads, and fetched the result once. I did not run it against the live API, so check the field names against the live OpenAPI at https://api.sume.com/reference/json before you rely on them.

A client-side timeout does not cancel the job. Cancel works only before generation work starts; afterward, POST /v1/jobs/{id}/cancel returns 409 job_generation_already_started.

What if the submit itself fails?

Retry the submit with the same Idempotency-Key. Do not generate a new key per attempt, or you pay twice. A 429 carrying rate_limited or queue_full should wait for retry-after when present, and Sume's error page lists the other codes: 402 insufficient_credits, 409 for job-state conflicts, 413 payload_too_large, and 415 unsupported_media_type. Nushell's http post raises on non-2xx, so wrap it in try when you want to read the error body.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume