मुख्य सामग्री पर जाएँ

Jobs

Some operations take longer than a single request should wait - process publishing and status changes, and bulk member imports sent with ?async=true. These return a job id you poll until the work finishes.

Anything that touches the chain - publishing a process, changing its status, relaying a vote - and bulk member imports sent with ?async=true run asynchronously. The write returns a jobId, and you poll one endpoint to learn the outcome. This is the async spine of the API.

Polling a job

The generic job endpoint is public - the job id is the capability. Poll it until status is completed or failed, then read the result. Everyone gets the status and counters; per-row import error detail (which can reference member data) is added only for a logged-in manager/admin (a dashboard session) - scoped API keys are treated as anonymous on this endpoint.

GET/jobs/{jobId}
curl -s "$B/jobs/$JOBID"     # public: status + counters (per-row errors only for a manager session)
const job = await client.jobs.get(jobId) // a single poll; see below to poll to completion
{ "jobId": "a1b2c3...",
  "type": "publish_voting_process",   // org_members | publish_voting_process | set_process_status |
                                      //   set_process_census | relay_vote | relay_votes
  "status": "completed",              // pending | completed | failed
  "result": { "status": "READY",      // on status change: the new status
              "processId": "",        // on relay_vote: the target on-chain election id (the
                                      //   question's upstreamId), readable while pending
              "nullifier": "",        // on relay_vote: the vote nullifier (readable while pending)
              "voteID": "" },         // on relay_vote: the nullifier as assigned on chain (on success)
  "errors": [] }                      // per-row import failures; omitempty (absent), manager session only
Field Type Description
jobId string Identifier returned when the work was enqueued.
type string What kind of work the job performs.
status string pending, completed or failed.
result object On success, details such as an address or vote id.
errors string[] Per-row import failures (line N: prefixed), returned only to a logged-in manager/admin (dashboard session); scoped API keys and anonymous callers get status and counters only. omitempty, so absent when empty.

Rules of thumb:

  • The call always returns 200, even for failures - branch on the status field.
  • completed: read result. failed: fail fast (don't keep polling); read errors for the reason (per-row import detail needs a manager/admin session). Anything else: keep polling (every ~2s is plenty).
poll to completion
// Polls until the job is done; throws JobFailedError on failure.
const job = await client.jobs.waitFor(jobId)
JsonElement job;
do { await Task.Delay(2000); job = await Get($"/jobs/{jobId}"); }
while (job.GetProperty("status").GetString() == "pending");
if (job.GetProperty("status").GetString() == "failed")
    throw new Exception(job.GetProperty("errors").ToString());
while True:
    job = get(f"/jobs/{jobId}").json()
    if job["status"] == "completed": break
    if job["status"] == "failed": raise RuntimeError(job["errors"])
    time.sleep(2)

Job types

  • org_members - bulk member import.
  • publish_voting_process - publishing a process (its census and one election per question, in one batch).
  • set_process_status - changing a question's status.
  • set_process_census - raising the on-chain maxCensusSize of live elections that need more room. See Census-update jobs.
  • relay_vote - relaying a single vote to the protocol.
  • relay_votes - relaying a batch of votes (POST /votes) to the protocol.

A relay_votes job reports every envelope of the batch, in the order they were sent. total is the batch size and added the number that have finished, so progress advances as the votes land; the job stays pending until the last one reports and ends completed only if every vote was accepted:

{ "jobId": "a1b2c3...", "type": "relay_votes", "status": "failed",
  "errors": ["vote 2: vote already exists"],
  "result": { "total": 3, "added": 3, "progress": 100,
    "votes": [
      { "processId": "9d3f...", "nullifier": "7ac1...", "voteID": "7ac1...", "status": "completed" },
      { "processId": "4e77...", "nullifier": "b209...", "voteID": "b209...", "status": "completed" },
      { "processId": "c015...", "nullifier": "33fa...", "status": "failed", "error": "vote already exists" } ] } }

Census-update jobs

A set_process_census job raises the on-chain maxCensusSize of live elections that need more room than they were published with. It is enqueued by:

The process endpoints return its id as jobId; member and group endpoints return any such ids in censusJobIds. No job is enqueued when no election needs to grow. result.questions holds one entry per question the job targeted (questionId, status, and error or noOp when they apply):

{ "jobId": "e5f6a7...", "type": "set_process_census", "status": "failed",
  "errors": ["1 of 2 census resizes failed: question 66b2...: <chain error>"],
  "result": { "questions": [
      { "questionId": "66b1...", "status": "completed" },
      { "questionId": "66b2...", "status": "failed", "error": "<chain error>" },
      { "questionId": "66b3...", "status": "completed", "noOp": true } ] } }
  • noOp: true means no transaction was sent: the election already had the room, or it was no longer open (READY or PAUSED) on chain - it ended while the job was queued, for example.
  • Every target is attempted, even after one fails, so the other resizes still apply. The job ends failed if any attempted resize failed, and its error names each failed question.
  • On success result.status is completed. A set_process_status job reports its questions in the same result.questions shape.

The members-job

A bulk member add is an org_members job - poll the same generic GET /jobs/{jobId}. Its result carries the import counters (added, total, progress); top-level errors carries any per-row failures:

curl -s "${auth[@]}" "$B/jobs/$JOBID"
const job = await client.jobs.get(jobId) // a single poll; jobs.waitFor(jobId) polls to completion
const { added, total, progress } = job.result ?? {}
{ "type": "org_members", "status": "pending",
  "result": { "added": 120, "total": 200, "progress": 60 } }   // errors omitempty: absent when empty

When present, each entry in errors is prefixed with line N: - the 1-based position of the offending member in the list you submitted - so you can map a failure back to its input row. Per-row errors detail is returned only to a logged-in manager/admin (a dashboard session); anonymous and API-key polls get the counters only. Because errors is omitempty it is absent (never []) when there is nothing to report.

Wait for result.progress: 100 before publishing a process whose census uses the members. See Members and groups.

Listing jobs

You can list an organization's jobs with pagination and an optional type filter to monitor recent imports and batch operations.

GET/jobs?orgAddress={address}

Listing needs a dashboard session

GET /jobs currently requires a logged-in manager/admin; scoped API keys are rejected (403). Poll a known job by id (GET /jobs/{jobId}) if you only have an API key.

Jobs expire

Member import jobs are cleared shortly after they complete. Read the final state promptly rather than relying on the job being available indefinitely.