Submissions & statuses
A submission is a request to prepare a return for one client from its uploaded documents. Creating one kicks off asynchronous processing.
Create
POST /v1/clients/{id}/submissions
| Field | Required | Notes |
|---|---|---|
tax_software |
✅ | Which tax software to prepare in (see below) |
tax_year |
✅ | 4-digit tax year. We accept the tax years the IRS is currently accepting returns for — today that's 2025. Anything else is rejected with a 400, so read the year from config rather than hard-coding it: the accepted set moves each filing season (a new year opens when the IRS begins accepting it, generally late January/February). |
proconnect_client_id |
— | Required only when tax_software is ProConnect |
proconnect_engagement_id |
— | Required only when tax_software is ProConnect |
Which documents get attached: every finalized document on the client that isn't already attached to a submission. Documents that went into an earlier submission stay with it — they are not re-submitted. So the flow is always: upload what's new, then create the submission.
ProConnect ids:
proconnect_client_idandproconnect_engagement_idcome from your Intuit ProConnect account — they link the submission to the right client and return there. See Link client and return for where to find them.
Resubmissions
If the client already has a completed submission for the same tax_year, creating another one is automatically treated as a resubmission — no extra fields required. The new submission inherits from the prior one (linking the return chain so we diff new documents against the prior version), and its response carries is_resubmission: true with resubmission_of pointing at the parent submission.
To resubmit, upload any new or corrected documents first, then create the submission as usual. (Documents already attached to the completed prior submission stay with it — a resubmission processes the newly uploaded input documents.) You still can't have two active submissions for a client at once, so wait for the prior one to complete before resubmitting.
Valid tax_software values
Drake · Lacerte · ProConnect · Axcess · UltraTax
A client can only have one active submission at a time. Creating a second while one is in flight returns
409— wait for the active one to complete first.
Statuses
GET /v1/submissions/{id} (or GET /v1/submissions to list, filterable by ?client_id= and ?status=; cursor-paginated).
The list returns a reduced submission (a
SubmissionSummary): every field below exceptinput_documentsandoutput_documents. Missing document arrays in a list row mean "not included here", not "this submission has no documents" — fetchGET /v1/submissions/{id}for the full object with its documents (e.g. to get a completed submission'soutput_documentsand theirdownload_urls).
status |
Meaning |
|---|---|
received |
Internal queueing state, rarely observed — a new submission is returned as processing. Never sent as a webhook. |
processing |
Being prepared |
completed |
Done — output_documents are ready to download |
cancelled |
Cancelled by you or by us |
The terminal states are completed and cancelled. There is no failed status — a submission that can't be prepared ends as cancelled, with a cancellation_code explaining why.
Each submission carries expected_completion_at — our 3 calendar day turnaround target, measured from when you create it. Webhooks are the recommended way to track a submission's status (see Webhooks); polling GET /v1/submissions/{id} is a supported fallback — if you poll, poll a few times a day, not in a tight loop.
Cancelling. You can cancel a submission within 30 minutes of creating it with POST /v1/submissions/{id}/cancel (wrong year/software, the client withdrew). After that window it must run to completion — contact us if you need it stopped. A cancel fires the submission.updated webhook with status: "cancelled", and you can pass an optional { "cancellation_reason": "..." }.
Shape
{
"id": 789,
"client_id": 123,
"status": "completed",
"tax_year": 2025,
"tax_software": "Drake",
"is_resubmission": false,
"resubmission_of": null,
"expected_completion_at": "2026-06-26T20:00:00Z",
"created_at": "2026-06-23T20:00:00Z",
"input_documents": [ { "id": 456, "filename": "W2.pdf", "download_url": "https://…" } ],
"output_documents": [
{ "id": 999, "filename": "..._Return.DI5", "category": "tax_return", "download_url": "https://…" },
{ "id": 998, "filename": "workpapers.pdf", "category": "workpapers", "download_url": "https://…" },
{ "id": 997, "filename": "..._notes.txt", "category": "review_notes", "download_url": "https://…" },
{ "id": 996, "filename": "..._notes.pdf", "category": "review_notes", "download_url": "https://…" }
]
}
- Output document
category— output documents carry acategoryso you can route them:tax_return(the tax-software return file, e.g. a Drake.DI5/ UltraTax.csd),workpapers(the workpapers PDF), andreview_notes(the reviewer's notes). An occasional output file may carry no category, so branch on the known values and keep a default. review_notescan be more than one document. The notes always ship as a.txt, and normally also as a rendered.pdf— both carrycategory: "review_notes". Collect all documents in the category rather than taking the first match, and pick by extension if you care which. The PDF is best-effort: if rendering fails the submission still completes and only the.txtis delivered, so don't make your flow depend on the PDF being there.- When
statusiscancelled, the submission also carriescancellation_reason(human-readable) andcancellation_code(stable; see Errors & cancellation codes).
Download URLs are presigned and time-limited; fetch them fresh from the submission when you need them.
Sandbox testing
You can test the full loop (create → upload → submit → complete/cancel) without a real preparer using a sandbox — a sandbox API key you create in the app portal (self-managed firms) or a dedicated sandbox organization (partners). Two force-transition endpoints let you drive a sandbox submission to a terminal state and validate your webhook handling. See Sandbox testing for how to get a sandbox, the endpoints, and an end-to-end walkthrough.