Polling & Jobs
How Steve represents asynchronous workflow progress and what to read from the polling endpoint.
GET /api/v1/jobs/{sessionId} is the polling endpoint for asynchronous workflow sessions.
It answers a different question from GET /api/v1/submissions/{submissionId}:
- the job endpoint tells you where the session is in its async lifecycle
- the submission endpoint gives you the canonical review-state read model
Endpoint
The session must belong to the same API key.
Workflow-session response
Field meanings
| Field | Meaning |
|---|---|
state | Session lifecycle status |
submissionStatus | Current public submission status when known |
result | Workflow-specific extraction payload |
failedReason | Processing failure reason when state=failed |
processedAt | ISO completion timestamp when available |
webhookDeliveryStatus | Delivery state for the configured production webhook |
For a newly created or still-running session, result, submissionStatus, and processedAt may be null.
Status values
| Status | Meaning | Terminal |
|---|---|---|
pending | Session exists but has not been submitted | No |
submitted | Session has been queued or is actively processing | No |
completed | Processing finished | Yes |
failed | Processing failed | Yes |
expired | Session timed out before submit | Yes |
A transient infrastructure failure can keep a job submitted (processing) longer while Steve retries automatically. Keep polling until the job reaches a terminal state; there is no fixed completion deadline.
Steve staff can also defer processing for a workflow, for example while you check a large import. Its submissions are accepted as usual and stay processing (the job stays submitted) until processing resumes.
A failed job is terminal unless you explicitly opt in to another capture-quality run with
POST /api/v1/submissions/{submissionId}/process-anyway. That endpoint reopens the same job as
submitted and clears its cached result. Resume polling the same session ID. Process anyway
clicked in the Steve admin panel does not reopen the job. A rerun updates batch outcome counts when it finishes; completed
batches stay completed and do not emit another batch.completed event.
Submission status values
When Steve has a linked submission, submissionStatus can be:
createdprocessingcapture_quality_blockedreviewapprovedsyncedrejectedcancelledfailed
capture_quality_blocked means processing stopped because the uploaded capture did not meet quality requirements. approved is the terminal status — treat it as "finished". synced is an additional status a submission reaches only when its data was actually delivered to Open Loyalty: it is owned by the delivery ledger, not by review actions, so a workflow that declares no delivery targets, an account with no Open Loyalty connection, and any skipped or outstanding delivery all report approved. This is the field to use when you need to know whether a completed session requires manual review.
Expiration model
Sessions are created with a 30-minute TTL. Expiration is finalized by a background sweep, so a stale unsubmitted session can remain pending briefly before it is marked expired.
Webhook delivery status
| Value | Meaning |
|---|---|
null | No delivery attempt is recorded yet, either because no webhook was configured or because the configured webhook has not yet been attempted |
pending | A delivery attempt failed and a retry is scheduled |
delivered | A target returned 2xx |
failed | Delivery exhausted its retry budget |
Recommended polling pattern
- poll every 5 to 10 seconds
- stop when
stateiscompleted,failed, orexpired - if
state=completedandsubmissionStatus=review, fetch the submission and enter your review flow - if webhooks are enabled, keep polling as a fallback and recovery path rather than the primary notification channel
Polling bulk uploads
When you create many sessions with the same batchRef, poll the batch aggregate instead of each job individually:
The response reports counts (total, processed, review, failed, approved) and a status of open or completed. Per-job polling remains available for drilling into individual sessions, but one batch request replaces thousands of job requests during a bulk import. See the batch endpoint reference and the batch.completed event in webhooks.
A batchRef is not reusable once its batch reaches completed. Completion is terminal, so creating a session with a completed batch's batchRef is rejected with 409 Conflict (type slug conflict) and the detail Batch '<batchRef>' has already completed and cannot accept new sessions. Use a new batchRef. Generate a fresh batchRef per import run rather than reusing one across runs; while a batch is still open, adding sessions to it is fine.