Quick Start
End-to-end guide for submitting files into Steve workflows through the external API.
This guide walks the production flow:
- discover accessible workflows
- create a workflow session
- upload files directly to Cloudflare R2
- submit the session for processing
- track completion by webhook or polling
- fetch the submission and, if needed, take a review action
Before you start
You need:
- a company-scoped API key created by a
super_admin - at least one workflow assigned to that company in the admin app
- the Convex site host for your Steve deployment
Steve API keys are long-lived server secrets. Do not embed them in browsers, mobile apps, or desktop binaries.
Optional: Use the TypeScript SDK
Steve ships a first-party TypeScript client in packages/steve-sdk.
Step 1: List workflows
Use the workflow list endpoint to discover what your company is allowed to integrate with and which workflow version is currently published.
Example response:
Use the upload block as the source of truth for:
- how many files to send
- which MIME types Steve accepts
- whether the workflow expects labeled file slots
- the maximum file size
To fetch the currently published summary for one workflow:
To fetch the immutable upload and output contract for a specific version:
Example contract response:
Use outputJsonSchema when you want machine validation. Use outputSchema when you want a simple typed field list for UI or mapping code.
Step 2: Create a workflow session
Creating a session reserves file slots, creates the linked submission record, and returns pre-signed R2 upload URLs.
Important request fields:
| Field | Required | Notes |
|---|---|---|
fileCount | Yes | Must stay within the workflow's minFiles..maxFiles range |
contentTypes | Yes | One MIME type per file slot |
clientSubmissionId | Yes | Idempotency key for the caller |
webhookUrl | No | Public HTTPS endpoint for production notifications |
metadata | No | Echoed back in jobs, submission detail, and webhook deliveries |
externalUser | No | End-user attribution ({ id?, name?, email? }) surfaced as "Submitted by" in the Steve admin UI. Omit to attribute to the API key. |
Example response:
Operational notes:
- upload URLs expire after 15 minutes
- the session expires after 30 minutes
- Steve currently exposes no public upload-URL refresh endpoint; if the pre-signs expire, create a new session
- if you retry
create sessionwith the sameclientSubmissionIdwhile the first session is still pending, Steve returns the existing session with200 OK
Step 3: Upload files directly to R2
Upload each file to its uploadUrl using PUT. The Content-Type header must match the MIME type you declared in contentTypes.
Steve does not proxy the file bytes. The upload goes straight to Cloudflare R2.
Step 4: Submit the session
Once every required file is uploaded, submit the session.
Response:
Steve verifies the files exist in R2 before it queues the session for processing. If required files are missing, the endpoint returns 422 with field-level errors for the missing file slots.
Step 5: Track completion
Use webhooks for production latency. Use polling as a fallback and for recovery.
Webhook deliveries
If you supplied a webhookUrl, Steve sends a signed POST when the session completes, fails, or when later submission actions change state.
See Webhooks for:
- delivery headers
- signature verification
- retry timing
- event types
Polling
Poll the job endpoint every 5 to 10 seconds:
Example workflow-session response:
Interpret the fields like this:
stateis the session lifecycle:pending,submitted,completed,failed, orexpiredsubmissionStatusis the current business outcome when known: for exampleprocessing,review,approved,rejected, orcancelledresultis the workflow-specific processing payloadresultshould be interpreted against the workflow version contract you fetched earlierwebhookDeliveryStatustells you whether Steve has delivered the configured webhook yet
Stop polling when state reaches completed, failed, or expired.
Step 6: Read the submission and take action if needed
When a completed session lands in submissionStatus: "review", fetch the submission and decide what to do next.
If the submission is in review:
- approve it with
POST /api/v1/submissions/{submissionId}/approve - reject it with
POST /api/v1/submissions/{submissionId}/reject - cancel it with
POST /api/v1/submissions/{submissionId}/cancel - resolve any fraud matches before approval
See Submission Review for the full review flow.
Status model
Session status
| Status | Meaning | Terminal |
|---|---|---|
pending | Session created, waiting for uploads and submit | No |
submitted | Session accepted for processing | No |
completed | Processing finished | Yes |
failed | Processing failed | Yes |
expired | Session aged out before submit | Yes |
Submission status
| Status | Meaning |
|---|---|
created | Submission record exists but processing has not started |
processing | Session was submitted and Steve is processing it |
review | Human decision required |
approved | Approved and eligible for downstream sync |
synced | Approved and synced downstream |
rejected | Rejected by reviewer |
cancelled | Cancelled by API call |
failed | Processing failed for a non-review reason |
Limits that matter in production
| Limit | Value |
|---|---|
| Pending sessions per API key | 20 |
| Session TTL | 30 minutes |
| Upload URL TTL | 15 minutes |
| Workflow list default/max page size | 20 / 100 |
| Metadata size | 4 KB serialized |
| Metadata nesting depth | 2 levels |