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 — a tax_return file 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), a workpapers PDF, and a review_notes file — each with a working download_url, alongside your original input_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 the tax_return is not a PDF. One difference to know: the sandbox returns a single review_notes file, whereas a real completion normally returns two (a .txt and a rendered .pdf) — so handle all documents in that category, not just the one the sandbox shows.

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 client201

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 submission201 (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 reason200 (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.

results matching ""

    No results matching ""