Errors & rate limits

Error shape

Errors return a non-2xx status and a JSON body with a stable, machine-readable code and a human-readable message:

{ "code": "insufficient_scope", "message": "API key is missing required scope 'submissions.write'" }

Branch on code, not message. The message wording may change; the code is stable (we add new codes rather than renaming existing ones). Every error carries a code — a generic one derived from the status, plus specific codes that disambiguate cases sharing a status.

Status Meaning Typical cause
400 Bad request Missing/invalid field (e.g. no tax_software, non-numeric tax_year), no documents uploaded
401 Unauthorized Missing, malformed, or unrecognized API key
403 Forbidden Key lacks the required scope; or a sandbox-only endpoint was called on a non-sandbox submission
404 Not found Unknown client/submission id (or not in your org)
409 Conflict Client already has an active submission; documents still uploading (call finalize first); or an Idempotency-Key conflict
413 Payload too large Direct multipart upload over 10 MB — use the presigned flow
429 Too Many Requests Rate limit exceeded — see below

Error codes

The codes that disambiguate same-status conditions (handle these differently):

code Status Meaning / what to do
missing_authorization 401 No Authorization header was provided.
missing_api_key 401 The Authorization header was present but carried no key.
malformed_api_key 401 The key isn't shaped like a mag_… key — usually truncated on copy. Re-copy the whole key.
invalid_api_key 401 Well-formed but unrecognized (revoked, or a typo that still parses).
insufficient_scope 403 The key isn't scoped for this endpoint.
partner_paused 403 The partner's API access is paused — new writes are blocked. Contact Magnetic.
partner_unlinked 403 The partner no longer manages this firm, so the key is inactive. Contact Magnetic.
sandbox_only 403 A sandbox-only control was called on something that isn't sandbox — a real submission, or (Partner API) a real firm. Only a firm's own admin can accept its management.
no_input_documents 400 The client has no finalized documents to submit — upload documents first.
invalid_limit 400 limit is not a positive integer, or is above the maximum — see Pagination.
invalid_cursor 400 cursor wasn't one we issued. Pass back next_cursor verbatim rather than constructing one.
invalid_content_md5 400 The Content-MD5 sent with a direct upload doesn't match the bytes received — the upload was corrupted in transit; retry it.
active_submission_exists 409 The client already has an in-flight submission — wait for it, or cancel it.
documents_still_uploading 409 An upload isn't finalized — call finalize, or delete an abandoned one (list the client's documents, then DELETE the one in state uploading).
document_attached 409 The document is attached to a submission and can't be deleted.
cancel_window_passed 409 The 30-minute cancellation window has passed; the submission must run to completion.
submission_already_terminal 409 The submission is already completed/cancelled.
invalid_idempotency_key 400 The Idempotency-Key header was sent empty, or is longer than 255 characters. Any non-blank string within that limit is accepted — a UUID is the usual choice. Omitting the header entirely is fine; sending a blank one is not, so a key generator that returns nothing fails loudly instead of silently dropping the guarantee.
firm_not_accepted 409 (Partner API) The firm hasn't accepted your management yet, so it can't be acted on — chase its admin, or wait for acceptance.
invitation_role_conflict 409 (Partner API) The address has a pending invitation at a different role and it couldn't be withdrawn. Retry shortly.
default_sandbox_protected 409 (Partner API) Your default sandbox can't be deleted.
firm_already_accepted 409 (Partner API) The firm accepted your management, so it can no longer be deleted — it's theirs now.
firm_revoked 409 (Partner API) This firm's management was revoked, so it can't be accepted. Contact Magnetic if it should be re-linked.
sandbox_limit_reached 409 (Partner API) You already hold the maximum number of sandbox firms. Delete one to free a slot.
idempotency_key_reuse 409 The key was already used for a different request. Use a new key for a new request, or resend the original body to replay it.
idempotency_request_in_progress 409 A request with this key is still running — retry shortly for its result. Also returned, for the rest of the key's 24 hours, if we couldn't record a response after the work succeeded; there, reconcile with a GET rather than waiting.
payload_too_large 413 A direct multipart upload exceeded the 10 MB limit — use the presigned flow.
rate_limited 429 Back off and retry after Retry-After (see below).

This table isn't exhaustive — other validation failures also carry a specific code (e.g. invalid_cursor, invalid_limit, invalid_content_md5 on a bad request). Generic per-status codes (bad_request 400, unauthorized 401, forbidden 403, not_found 404, conflict 409, payload_too_large 413, unprocessable_entity 422, internal_error 500) are used where no more specific code applies. In all cases, branch on code and treat an unrecognized one by its HTTP status.

Rate limits

Both write endpoints and the submission status reads (GET /v1/submissions/{id} and GET /v1/submissions) are rate limited per API key. We don't publish specific limits (they may change), so design for the 429 rather than a fixed number. When you exceed the limit you get a 429 with a Retry-After header (seconds) and a matching body field:

{ "code": "rate_limited", "message": "Rate limit exceeded", "retry_after_seconds": 42 }

Back off and retry after the delay — read it from either the Retry-After header or retry_after_seconds.

Webhooks are the recommended way to track a submission's status; polling GET /v1/submissions/{id} is a supported fallback. Configure a submission_updated_webhook_url on your API key and we'll push a signed submission.updated event on every status change (see Webhooks) — no polling loop to rate-limit. If you do poll, poll a few times a day (not in a tight loop), and honor Retry-After on a 429.

Idempotency & retries

  • Don't double-submit: a client allows only one active submission (409). Track the submission id you got back rather than re-creating.
  • Network errors on create are not automatically safe to retry — a timed-out POST may still have succeeded server-side. If a create times out, GET /v1/clients or GET /v1/submissions to confirm whether the resource was created before sending another create request.
  • Download URLs (download_url) are presigned and expire — re-fetch them from the submission instead of caching long-term.

Cancellation codes

When a submission's status is cancelled, the submission carries a human-readable cancellation_reason and a stable, machine-readable cancellation_code you can branch on. The codes are stable — we add new ones rather than renaming existing ones.

cancellation_code Meaning
encrypted_file A submitted file was password-protected/encrypted. Provide an unlocked file and resubmit.
unsupported_file A submitted file type is not supported. Convert to a supported type and resubmit.
empty_file A submitted file was empty (0 bytes).
outdated_tax_software_file A tax-software file was from a prior tax year. Provide a current-year file.
unextractable_zip A submitted ZIP could not be extracted (corrupt/unsupported archive).
mismatched_client Uploaded documents didn't match the client on the submission.
proconnect_link_failed The linked ProConnect return could not be opened.
unsupported_return_type The return type isn't currently supported (e.g. Form 1041 estates/trusts).
cancelled_by_firm Cancelled by a member of the firm.
client_deleted The client was deleted, so its in-flight submission was cancelled automatically. Nobody had started work on it — see deleting a client.
other Cancelled for a reason without a more specific code.

Some cancellations are the taxpayer's/firm's to resolve (e.g. encrypted_file, mismatched_client); as an integrator you'll typically forward those back to the firm. Because you talk to us on the firm's behalf, decide per code whether to surface it to the firm or handle it yourself.

results matching ""

    No results matching ""