Reconcile a no-code run with GET /v1/jobs and idempotency_key

When a Zap, scenario or flow loses its job ids, list jobs with GET /v1/jobs and join on idempotency_key. Pages are newest first, 100 at most, cursor-paged.

5 min readSume
All posts

Set the Idempotency-Key on every Sume submit to something derived from your own record id, then rebuild lost state with GET /v1/jobs: each job row carries the idempotency_key it was created with, and Sume's OpenAPI says that is the column to join your own labels on. Pages are newest first, capped at 100, and move with next_cursor, passed back as starting_after. It works the same whether the run lived in n8n, Make, Zapier, Activepieces or a script.

Everything here is from Sume's API reference, the live OpenAPI behind it, and Jobs and results.

Which query parameters does the list take?

From the OpenAPI definition of listApiJobs. An unrecognized parameter is a 400 unknown_parameter rather than a silently unfiltered page, so a typo cannot hand you the wrong rows.

Parameters of GET /v1/jobs from Sume's OpenAPI, read 2026-10-02.
ParameterValuesUse
statusqueued, processing, completed, failed, canceledFind stuck or failed jobs
typeNon-empty stringNarrow to one job type
limit1 to 100Page size
starting_afterThe previous page's next_cursorNext page
scopethread or workspaceAn ordinary API key defaults to workspace

Why join on idempotency_key and not array position?

Sume's description says newest-first is not your submission order once a wave retries, so identity must not come from position. A retry with the same Idempotency-Key returns the original job, so one key maps to one job id, and the row shows both. Use a key that encodes your record, such as row-1042-v1; the submit endpoints accept 1 to 255 printable ASCII characters.

Reuse the same key only for the same operation and payload. If you change the intent, change the key.

What does a reconcile script look like?

This prints the failed jobs with their keys. The last page has no next_cursor, which is the loop terminator; Sume says not to synthesize one from the final job.

import json, os, urllib.request

def get(path):
    req = urllib.request.Request(
        "https://api.sume.com" + path,
        headers={"Authorization": "Bearer " + os.environ["SUME_API_KEY"]},
    )
    with urllib.request.urlopen(req, timeout=30) as r:
        return json.load(r)

def main():
    cursor = None
    while True:
        q = "/v1/jobs?limit=100&status=failed"
        if cursor:
            q += "&starting_after=" + cursor
        data = get(q)["data"]
        for job in data["jobs"]:
            print(job.get("idempotency_key"), job["id"])
        cursor = data.get("next_cursor")
        if not cursor:
            break

main()

What do I do with a failed or missing job?

For a failed job, read the error off the job record: GET /v1/jobs/{id}, and fix the cause the category names (for example validation means fix the input). Retry the submit only after that, with a new key if the input changed. For a job that is completed while your sheet says pending, the webhook never reached you: fetch GET /v1/jobs/{id}/result and write it back, or call POST /v1/jobs/{job_id}/webhook/redeliver so the event arrives again. Never resubmit the original paid request just because your tool timed out; a local timeout does not cancel the job.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume