Sandbox testing
Sandbox lets you exercise the full loop — create → upload → submit → complete/cancel — without a real preparer: sandbox submissions never reach a human and send no emails. There are two ways to get one, depending on how you integrate:
- Self-managed firm: create a sandbox API key in the app portal (Preferences → Self-managed API → Create API key → Sandbox key). Every submission made with that key is a sandbox submission, against your firm's own organization. Nothing else changes — you use the same endpoints with your sandbox key instead of a live one.
- Partner: use your dedicated sandbox organization (provisioned with your partner account) and create a key for it in the partner console — set that key's webhook URL the same way you would a firm key.
The base URL is the same for sandbox and live — https://api.magnetictax.com — the key selects the context. Either way the key's submissions are flagged as sandbox, so the whole rest of this page works the same. Because a sandbox submission won't be worked by a human, two sandbox-only endpoints let you push it to a terminal state to test your webhook/polling handling:
| Method | Path | Effect |
|---|---|---|
| POST | /v1/submissions/{id}/sandbox/complete |
Force the submission to completed |
| POST | /v1/submissions/{id}/sandbox/cancel |
Force the submission to cancelled (optional body {"cancellation_reason": "..."}) |
Both fire your configured webhook (so you can validate delivery + signatures) and send no emails. They return 403 unless the submission is a sandbox submission (created with a sandbox key or in a sandbox org), and 409 if the submission is already terminal.
What documents come back on
sandbox/complete? To let you exercise your download path end-to-end, a completed sandbox submission returns sample output documents — atax_returnfile in the software's native format (e.g. a Drake.DI5, an UltraTax.csd; cloud/fileless software like ProConnect has no native file, so a PDF stand-in is used), aworkpapersPDF, and areview_notesfile — each with a workingdownload_url, alongside your originalinput_documents. They're clearly-labelled placeholders (fictional content), not a real prepared return — no preparer runs on a sandbox submission. Their shape (categories, filenames + extensions, download URLs) matches a real completion, so your ingestion code can be tested exactly as it would run in production — including that thetax_returnis not a PDF. One difference to know: the sandbox returns a singlereview_notesfile, whereas a real completion normally returns two (a.txtand a rendered
End-to-end sandbox walkthrough
The exact call sequence and the responses you'll get (real values from a live sandbox run; ids will differ). $BASE is the API base URL and $KEY is your sandbox mag_… key.
1. Create a client → 201
curl -s -X POST "$BASE/v1/clients" -H "Authorization: Bearer $KEY" \
-H 'Content-Type: application/json' -d '{"friendly_name":"Jane Q. Taxpayer"}'
{ "id": 812, "friendly_name": "Jane Q. Taxpayer", "taxpayer_tin": null, "created_at": "…" }
2. Upload a document (multipart shown; presigned also works) → 200
curl -s -X POST "$BASE/v1/clients/812/documents" -H "Authorization: Bearer $KEY" \
-F [email protected]
{ "documents": [ { "id": 6175, "filename": "W2.pdf", "download_url": "https://…" } ], "failed_document_ids": [], "failed_document_indices": [] }
Read failures from failed_document_ids; failed_document_indices is deprecated (see Deprecations). On this direct-upload path failed_document_ids is always empty (a failed direct upload has no id).
3. Create a submission → 201 (status:"processing")
curl -s -X POST "$BASE/v1/clients/812/submissions" -H "Authorization: Bearer $KEY" \
-H 'Content-Type: application/json' -d '{"tax_software":"Drake","tax_year":2025}'
{ "id": 849, "status": "processing", "tax_software": "Drake", "tax_year": 2025, "is_resubmission": false, "input_documents": [ … ], "output_documents": [] }
→ webhook fires: {"event":"submission.updated","submission":{"id":849,"status":"processing",…}} (signed X-Magnetic-Signature).
4. Check status — a webhook fires on every transition (see below); you can also read the submission directly (still processing until you advance it) → 200
curl -s "$BASE/v1/submissions/849" -H "Authorization: Bearer $KEY"
5. Force-complete (sandbox only — stands in for the preparer) → 200 (status:"completed")
curl -s -X POST "$BASE/v1/submissions/849/sandbox/complete" -H "Authorization: Bearer $KEY"
{
"id": 849, "status": "completed",
"input_documents": [ … ],
"output_documents": [
{ "id": 902, "filename": "2025-Drake-return.DI5", "category": "tax_return",
"download_url": "https://s3-us-west-2.amazonaws.com/..." },
{ "id": 903, "filename": "2025-sandbox-example-workpapers.pdf", "category": "workpapers",
"download_url": "https://s3-us-west-2.amazonaws.com/..." },
{ "id": 904, "filename": "2025-sandbox-example-review-notes.pdf", "category": "review_notes",
"download_url": "https://s3-us-west-2.amazonaws.com/..." }
]
}
→ webhook fires: submission.updated with status:"completed". The output_documents are sample placeholders (see the note above) — fetch each download_url to exercise your ingestion path.
6. (Alternative) Force-cancel with a reason → 200 (status:"cancelled" + a stable cancellation_code)
curl -s -X POST "$BASE/v1/submissions/850/sandbox/cancel" -H "Authorization: Bearer $KEY" \
-H 'Content-Type: application/json' -d '{"cancellation_reason":"The file '\''w2.pdf'\'' is password-protected. Please unlock the PDF and resubmit."}'
{ "id": 850, "status": "cancelled", "cancellation_code": "encrypted_file",
"cancellation_reason": "The file 'w2.pdf' is password-protected. …" }
→ webhook fires: submission.updated with status:"cancelled". (See cancellation codes.)
That's the whole loop: create client → upload → create submission → (webhook: processing) → sandbox complete/cancel → (webhook: completed/cancelled) — no preparer, no firm emails, and every transition delivers a signed webhook so you can validate your handler end to end.