{"errors":[{"code":"unauthorized","http_status":401,"where":["any authenticated request"],"job_failure":false,"meaning":"The API key is missing or not valid.","fix":"Send it as 'Authorization: Bearer <key>' and check it was copied completely.","retryable":false,"retry":"Fix the key; do not retry with the same key.","message":"The API key is missing or not valid: send it as 'Authorization: Bearer <key>' and check it was copied completely.","details":{}},{"code":"rate_limited","http_status":429,"where":["any authenticated request","POST /v1/claim"],"job_failure":false,"meaning":"Too many requests.","fix":"Wait the number of seconds in the Retry-After header, then try again.","retryable":true,"retry":"Wait the seconds in the Retry-After header, then repeat the same request.","message":"Too many requests: wait the number of seconds in the Retry-After header, then try again.","details":{}},{"code":"daily_quota_exceeded","http_status":429,"where":["POST /v1/jobs"],"job_failure":false,"meaning":"This key has used all of today's jobs.","fix":"Try again after 00:00 UTC, or ask mutual to raise the quota.","retryable":false,"retry":"Stop and tell the human: the key's daily jobs are used up until 00:00 UTC (Retry-After gives the seconds); mutual can raise the quota.","message":"This key has used all of today's jobs: try again after 00:00 UTC, or ask mutual to raise the quota.","details":{}},{"code":"check_limit_reached","http_status":429,"where":["POST /v1/check"],"job_failure":false,"meaning":"This account has used today's pre-flight checks.","fix":"Submit the job directly (it runs the same checks), or check again after 00:00 UTC.","retryable":false,"retry":"Not today: this account's pre-flight checks are used up until 00:00 UTC (Retry-After gives the seconds); submit the job directly, it runs the same checks.","message":"This account has used today's pre-flight checks: submit the job directly (it runs the same checks), or check again after 00:00 UTC.","details":{}},{"code":"server_busy","http_status":503,"where":["POST /v1/jobs"],"job_failure":false,"meaning":"Zap! is busy.","fix":"Too many jobs are waiting right now (jobs_waiting, estimated_wait_min); submit again after the Retry-After seconds, or see https://zap.mutual.solutions/status.","retryable":true,"retry":"Wait the seconds in the Retry-After header, then submit again; estimated_wait_min is the expected wait.","message":"Zap! is busy: too many jobs are waiting right now (jobs_waiting, estimated_wait_min); submit again after the Retry-After seconds, or see https://zap.mutual.solutions/status.","details":{}},{"code":"too_many_active_jobs","http_status":429,"where":["POST /v1/jobs"],"job_failure":false,"meaning":"This key already has the maximum number of jobs queued or running.","fix":"Wait for one to finish, then submit again.","retryable":true,"retry":"Wait until one of your queued or running jobs finishes, then submit again.","message":"This key already has the maximum number of jobs queued or running: wait for one to finish, then submit again.","details":{}},{"code":"too_large","http_status":413,"where":["POST /v1/jobs","POST /v1/check"],"job_failure":false,"meaning":"The upload is over 20 MiB in total.","fix":"Send the board gzip-compressed (board.kicad_pcb.gz in the same board field, up to 64 MiB unpacked; zap.py, the MCP tool and the web page do this for you) and only the .kicad_pcb (and .kicad_pro); if the board itself is that large, remove embedded 3D models or images (Footprint Properties > 3D Models) and save again.","retryable":false,"retry":"Send smaller files.","message":"The upload is over 20 MiB in total: send the board gzip-compressed (board.kicad_pcb.gz in the same board field, up to 64 MiB unpacked; zap.py, the MCP tool and the web page do this for you) and only the .kicad_pcb (and .kicad_pro); if the board itself is that large, remove embedded 3D models or images (Footprint Properties > 3D Models) and save again.","details":{}},{"code":"board_too_large_unpacked","http_status":413,"where":["POST /v1/jobs","POST /v1/check"],"job_failure":false,"meaning":"The compressed board unpacks to more than 64 MiB (max_unpacked_bytes).","fix":"Remove embedded 3D models or images (Footprint Properties > 3D Models), save again and send it again.","retryable":false,"retry":"Send a smaller board.","message":"The compressed board unpacks to more than 64 MiB (max_unpacked_bytes): remove embedded 3D models or images (Footprint Properties > 3D Models), save again and send it again.","details":{}},{"code":"invalid_compressed_board","http_status":400,"where":["POST /v1/jobs","POST /v1/check"],"job_failure":false,"meaning":"The board looks gzip-compressed but cannot be unpacked (cut short, damaged, or more than one file).","fix":"Compress the .kicad_pcb again with gzip, or send it uncompressed.","retryable":false,"retry":"Send the board again, compressed whole with gzip or not compressed.","message":"The board looks gzip-compressed but cannot be unpacked (cut short, damaged, or more than one file): compress the .kicad_pcb again with gzip, or send it uncompressed.","details":{}},{"code":"length_required","http_status":411,"where":["POST /v1/jobs","POST /v1/check"],"job_failure":false,"meaning":"The upload has no total length (for example it was sent with Transfer-Encoding: chunked), which Zap! cannot read.","fix":"Send a normal multipart upload with a Content-Length (curl -F, Python requests and zap.py do this).","retryable":false,"retry":"Send a normal multipart upload with a Content-Length.","message":"The upload has no total length (for example it was sent with Transfer-Encoding: chunked), which Zap! cannot read: send a normal multipart upload with a Content-Length (curl -F, Python requests and zap.py do this).","details":{}},{"code":"board_required","http_status":400,"where":["POST /v1/jobs","POST /v1/check"],"job_failure":false,"meaning":"No board was attached.","fix":"Send the placed board in the multipart field 'board' (curl -F board=@myboard.kicad_pcb).","retryable":false,"retry":"Put the board in the multipart field 'board'.","message":"No board was attached: send the placed board in the multipart field 'board' (curl -F board=@myboard.kicad_pcb).","details":{}},{"code":"invalid_board","http_status":422,"where":["POST /v1/jobs","POST /v1/check","job failure"],"job_failure":true,"meaning":"The file could not be read as a KiCad board.","fix":"Open the board in KiCad's PCB Editor, use File > Save, and upload that .kicad_pcb (not a schematic, Gerber or zip).","retryable":false,"retry":"Fix the board file; resubmitting the same file gives the same answer.","message":"The file could not be read as a KiCad board: open the board in KiCad's PCB Editor, use File > Save, and upload that .kicad_pcb (not a schematic, Gerber or zip).","details":{"blank_lines":"The file has more than 10,000 blank lines, which KiCad never writes: save it again in KiCad (File > Save) and upload that .kicad_pcb.","incomplete":"The file is incomplete (its brackets do not close, so it was probably cut off while copying or uploading): save it again in KiCad (File > Save) and upload the complete .kicad_pcb.","no_outline":"The board has no outline: draw it on the Edge.Cuts layer in KiCad (select Edge.Cuts, then Place > Draw Lines or Draw Rectangle), save, and upload again."}},{"code":"invalid_project","http_status":400,"where":["POST /v1/jobs","POST /v1/check"],"job_failure":false,"meaning":"The project file is not a valid KiCad project.","fix":"Attach the .kicad_pro that KiCad writes next to the board when you use File > Save in the PCB Editor, or leave it out.","retryable":false,"retry":"Attach the right .kicad_pro or leave it out.","message":"The project file is not a valid KiCad project: attach the .kicad_pro that KiCad writes next to the board when you use File > Save in the PCB Editor, or leave it out.","details":{}},{"code":"invalid_rules","http_status":400,"where":["POST /v1/jobs","POST /v1/check"],"job_failure":false,"meaning":"The custom rules file is not valid.","fix":"Attach the .kicad_dru that KiCad saves from File > Board Setup > Design Rules > Custom Rules (its first line starts with '(version'), or leave it out.","retryable":false,"retry":"Attach the right .kicad_dru or leave it out.","message":"The custom rules file is not valid: attach the .kicad_dru that KiCad saves from File > Board Setup > Design Rules > Custom Rules (its first line starts with '(version'), or leave it out.","details":{}},{"code":"invalid_currents","http_status":422,"where":["POST /v1/jobs","POST /v1/check"],"job_failure":false,"meaning":"The currents file is not valid.","fix":"Send JSON {\"nets\": {\"<net name>\": {\"amps\": A}}} with net names exactly as KiCad shows them (Inspect > Net Inspector), amps from 0 to 100 and at most 2,000 nets (names not found in the board are listed in unknown_nets).","retryable":false,"retry":"Fix the currents JSON (see unknown_nets / ambiguous_nets in the answer).","message":"The currents file is not valid: send JSON {\"nets\": {\"<net name>\": {\"amps\": A}}} with net names exactly as KiCad shows them (Inspect > Net Inspector), amps from 0 to 100 and at most 2,000 nets (names not found in the board are listed in unknown_nets).","details":{}},{"code":"invalid_router","http_status":422,"where":["POST /v1/jobs","POST /v1/check"],"job_failure":false,"meaning":"The router field is not valid.","fix":"Send stable or next, or leave it out (which means stable).","retryable":false,"retry":"Send router=stable, router=next, or leave it out.","message":"The router field is not valid: send stable or next, or leave it out (which means stable).","details":{}},{"code":"router_unavailable","http_status":409,"where":["POST /v1/jobs","POST /v1/check"],"job_failure":false,"meaning":"The next router is not available right now.","fix":"Use stable.","retryable":false,"retry":"Use router=stable (GET /v1/version lists routers.next when it is offered).","message":"The next router is not available right now; use stable.","details":{}},{"code":"invalid_consent","http_status":422,"where":["POST /v1/jobs","POST /v1/check"],"job_failure":false,"meaning":"The consent_training field is not valid.","fix":"Send true or false, or leave it out (which means false).","retryable":false,"retry":"Send consent_training=true or false, or leave it out.","message":"The consent_training field is not valid: send true or false, or leave it out (which means false).","details":{}},{"code":"invalid_keep_existing","http_status":422,"where":["POST /v1/jobs","POST /v1/check"],"job_failure":false,"meaning":"The keep_existing field is not valid.","fix":"Send true or false, or leave it out (which means false).","retryable":false,"retry":"Send keep_existing=true or false, or leave it out.","message":"The keep_existing field is not valid: send true or false, or leave it out (which means false).","details":{}},{"code":"keep_existing_unavailable","http_status":409,"where":["POST /v1/jobs","POST /v1/check"],"job_failure":false,"meaning":"The router you chose cannot keep existing copper.","fix":"Use router=stable, or remove the tracks and vias in KiCad (Edit > Global Deletions, tick Tracks and Vias) and send keep_existing=false.","retryable":false,"retry":"Use router=stable, or send keep_existing=false with a board that has no tracks or vias.","message":"The router you chose cannot keep existing copper: use router=stable, or remove the tracks and vias in KiCad (Edit > Global Deletions, tick Tracks and Vias) and send keep_existing=false.","details":{}},{"code":"invalid_fab_floor","http_status":422,"where":["POST /v1/jobs","POST /v1/check"],"job_failure":false,"meaning":"The fab_floor field is not valid.","fix":"Send jlc (the default: never below JLC's capability) or board (your board's own minimums, for fabs finer than JLC), or leave it out.","retryable":false,"retry":"Send fab_floor=jlc or fab_floor=board, or leave it out.","message":"The fab_floor field is not valid: send jlc (the default: never below JLC's capability) or board (your board's own minimums, for fabs finer than JLC), or leave it out.","details":{}},{"code":"fab_floor_unavailable","http_status":409,"where":["POST /v1/jobs","POST /v1/check"],"job_failure":false,"meaning":"The router you chose cannot follow your board's own minimums yet.","fix":"Send fab_floor=jlc, or use router=next when GET /v1/version lists one.","retryable":false,"retry":"Send fab_floor=jlc, or use router=next when GET /v1/version lists one.","message":"The router you chose cannot follow your board's own minimums yet: send fab_floor=jlc, or use router=next when GET /v1/version lists one.","details":{}},{"code":"invalid_option","http_status":422,"where":["POST /v1/jobs","POST /v1/check"],"job_failure":false,"meaning":"A routing option is not valid (option names it, fix says what to send).","fix":"Correct it, or leave it out to use the default.","retryable":false,"retry":"Change the option named in option as fix says, or leave it out to use the default.","message":"A routing option is not valid (option names it, fix says what to send): correct it, or leave it out to use the default.","details":{}},{"code":"options_unavailable","http_status":409,"where":["POST /v1/jobs","POST /v1/check"],"job_failure":false,"meaning":"Routing options are not switched on yet.","fix":"Leave them out to use the defaults (GET /v1/version shows \"options\": true once they are on).","retryable":false,"retry":"Leave the routing options out for now (GET /v1/version lists options when they are on).","message":"Routing options are not switched on yet: leave them out to use the defaults (GET /v1/version shows \"options\": true once they are on).","details":{}},{"code":"input_has_copper","http_status":422,"where":["POST /v1/jobs","POST /v1/check","job failure"],"job_failure":true,"meaning":"The board already has tracks or vias.","fix":"Send strip_existing=true to discard them and route everything, keep_existing=true to keep them and route only the missing connections, or remove them in KiCad (Edit > Global Deletions, tick Tracks and Vias) and upload again.","retryable":false,"retry":"Send strip_existing=true to discard them and route everything, or keep_existing=true to route around them.","message":"The board already has tracks or vias: send strip_existing=true to discard them and route everything, keep_existing=true to keep them and route only the missing connections, or remove them in KiCad (Edit > Global Deletions, tick Tracks and Vias) and upload again.","details":{}},{"code":"too_many_pads","http_status":422,"where":["POST /v1/jobs","POST /v1/check"],"job_failure":false,"meaning":"The board has more than 10,000 pads, the current limit.","fix":"Contact hello@mutual.solutions if you need a larger board routed.","retryable":false,"retry":"Not retryable: ask mutual for a larger limit.","message":"The board has more than 10,000 pads, the current limit: contact hello@mutual.solutions if you need a larger board routed.","details":{}},{"code":"too_many_footprints","http_status":422,"where":["POST /v1/jobs","POST /v1/check"],"job_failure":false,"meaning":"The board has more than 4,000 footprints, the current limit.","fix":"Contact hello@mutual.solutions if you need a larger board routed.","retryable":false,"retry":"Not retryable: ask mutual for a larger limit.","message":"The board has more than 4,000 footprints, the current limit: contact hello@mutual.solutions if you need a larger board routed.","details":{}},{"code":"board_too_large","http_status":422,"where":["POST /v1/jobs","POST /v1/check","job failure"],"job_failure":true,"meaning":"The board needs more memory per routing attempt than the self-serve limit (memory gives the predicted GB, the outline, the copper layers and the grid cells).","fix":"For boards this large, email hello@mutual.solutions to arrange a business (B2B) job.","retryable":false,"retry":"Not retryable as is: email hello@mutual.solutions to arrange a business (B2B) job.","message":"The board needs more memory per routing attempt than the self-serve limit (memory gives the predicted GB, the outline, the copper layers and the grid cells): for boards this large, email hello@mutual.solutions to arrange a business (B2B) job.","details":{}},{"code":"unsupported_layer_count","http_status":422,"where":["POST /v1/jobs","POST /v1/check"],"job_failure":false,"meaning":"The board must have 2 to 16 copper layers.","fix":"Set the count in File > Board Setup > Board Stackup > Physical Stackup (Copper layers) and save.","retryable":false,"retry":"Fix the stackup in KiCad, then submit again.","message":"The board must have 2 to 16 copper layers: set the count in File > Board Setup > Board Stackup > Physical Stackup (Copper layers) and save.","details":{}},{"code":"not_found","http_status":404,"where":["GET /v1/jobs/{id}","DELETE /v1/jobs/{id}","GET /v1/jobs/{id}/result","GET /v1/jobs/{id}/report.json","GET /v1/jobs/{id}/report/{layer}.svg","POST /v1/feedback","any unknown address","POST /v1/jobs/{id}/snapshot"],"job_failure":false,"meaning":"No job with this id exists for this key.","fix":"Check the id and use the same key that submitted the job.","retryable":false,"retry":"Check the job id and use the key that submitted it.","message":"No job with this id exists for this key: check the id and use the same key that submitted the job.","details":{"path":"Not found: check the URL; the endpoints are listed at /docs."}},{"code":"not_ready","http_status":409,"where":["GET /v1/jobs/{id}/result","GET /v1/jobs/{id}/report.json","GET /v1/jobs/{id}/report/{layer}.svg","POST /v1/jobs/{id}/snapshot"],"job_failure":false,"meaning":"The job is still queued or running.","fix":"Poll GET /v1/jobs/{id} until the status is 'succeeded'.","retryable":true,"retry":"Poll GET /v1/jobs/{id} every 5 s until status is succeeded or failed, then download.","message":"The job is still queued or running: poll GET /v1/jobs/{id} until the status is 'succeeded'.","details":{}},{"code":"no_result","http_status":409,"where":["GET /v1/jobs/{id}/result","GET /v1/jobs/{id}/report/{layer}.svg","POST /v1/jobs/{id}/snapshot"],"job_failure":false,"meaning":"The job failed, so there is no routed board.","fix":"See the error in GET /v1/jobs/{id}.","retryable":false,"retry":"The job failed: read error.code in GET /v1/jobs/{id}.","message":"The job failed, so there is no routed board: see the error in GET /v1/jobs/{id}.","details":{}},{"code":"job_cancelled","http_status":409,"where":["GET /v1/jobs/{id}/result","GET /v1/jobs/{id}/report.json","GET /v1/jobs/{id}/report/{layer}.svg","POST /v1/jobs/{id}/snapshot"],"job_failure":false,"meaning":"The job was cancelled, so there is no routed board.","fix":"Submit the board again if you still need it.","retryable":false,"retry":"The job was cancelled (DELETE /v1/jobs/{id}); submit the board again if you still need it.","message":"The job was cancelled, so there is no routed board: submit the board again if you still need it.","details":{}},{"code":"expired","http_status":410,"where":["GET /v1/jobs/{id}/result","GET /v1/jobs/{id}/report.json","GET /v1/jobs/{id}/report/{layer}.svg","POST /v1/jobs/{id}/snapshot"],"job_failure":false,"meaning":"The routed board was deleted (7 days after the job, or on request).","fix":"Submit the board again.","retryable":false,"retry":"The routed board was deleted: submit the board again.","message":"The routed board was deleted (7 days after the job, or on request): submit the board again.","details":{}},{"code":"method_not_allowed","http_status":405,"where":["any address"],"job_failure":false,"meaning":"This address does not accept that method.","fix":"See /docs for the endpoints.","retryable":false,"retry":"Use the method the endpoint documents.","message":"This address does not accept that method: see /docs for the endpoints.","details":{}},{"code":"bad_request","http_status":400,"where":["POST /v1/jobs","POST /v1/check","any request"],"job_failure":false,"meaning":"The request could not be read.","fix":"Send a multipart/form-data upload as shown in /docs.","retryable":false,"retry":"Fix the request shape (see /docs.md).","message":"The request could not be read: send a multipart/form-data upload as shown in /docs.","details":{}},{"code":"check_busy","http_status":503,"where":["POST /v1/check","POST /v1/jobs","POST /v1/jobs/{id}/snapshot"],"job_failure":false,"meaning":"Several board checks are running right now.","fix":"Try again in a minute.","retryable":true,"retry":"Wait the seconds in Retry-After, then repeat the check.","message":"Several board checks are running right now: try again in a minute.","details":{}},{"code":"internal","http_status":500,"where":["any authenticated request","job failure"],"job_failure":true,"meaning":"Something went wrong on our side.","fix":"Try again, and if it repeats send the job id to hello@mutual.solutions.","retryable":true,"retry":"Retry once; if it repeats, send the job id to hello@mutual.solutions.","message":"Something went wrong on our side: try again, and if it repeats send the job id to hello@mutual.solutions.","details":{}},{"code":"timeout","http_status":null,"where":["job failure"],"job_failure":true,"meaning":"The job hit the 60-minute limit.","fix":"Tell hello@mutual.solutions the job id; a board this size may need a larger limit.","retryable":false,"retry":"Not retryable as is: send the job id to hello@mutual.solutions.","message":"The job hit the 60-minute limit: tell hello@mutual.solutions the job id; a board this size may need a larger limit.","details":{}},{"code":"timeout_or_limit","http_status":null,"where":["job failure"],"job_failure":true,"meaning":"The job hit the time or memory limit (very large outlines can exceed the memory).","fix":"Tell hello@mutual.solutions the job id; a board this size may need a larger limit.","retryable":false,"retry":"Not retryable as is: send the job id to hello@mutual.solutions.","message":"The job hit the time or memory limit (very large outlines can exceed the memory): tell hello@mutual.solutions the job id; a board this size may need a larger limit.","details":{}},{"code":"route_failed","http_status":null,"where":["job failure"],"job_failure":true,"meaning":"The router could not produce a result for this board.","fix":"Check in KiCad that the outline on Edge.Cuts is closed and every footprint is inside it (Inspect > Design Rules Checker shows both), then send the job id to hello@mutual.solutions if it repeats.","retryable":false,"retry":"Fix the board outline / placement; send the job id if it repeats.","message":"The router could not produce a result for this board: check in KiCad that the outline on Edge.Cuts is closed and every footprint is inside it (Inspect > Design Rules Checker shows both), then send the job id to hello@mutual.solutions if it repeats.","details":{}},{"code":"rules_check_failed","http_status":null,"where":["job failure"],"job_failure":true,"meaning":"KiCad could not check the routed board with the rules you sent.","fix":"Attach the .kicad_pro (and .kicad_dru) saved with this board (File > Save; rules in File > Board Setup > Design Rules), or leave them out to use KiCad's defaults.","retryable":false,"retry":"Send matching .kicad_pro/.kicad_dru or leave them out, then submit again.","message":"KiCad could not check the routed board with the rules you sent: attach the .kicad_pro (and .kicad_dru) saved with this board (File > Save; rules in File > Board Setup > Design Rules), or leave them out to use KiCad's defaults.","details":{}},{"code":"unsupported_rules","http_status":null,"where":["POST /v1/check","job failure"],"job_failure":true,"meaning":"Your .kicad_dru uses a custom rule condition Zap! cannot apply yet (for example insideCourtyard()).","fix":"Remove or simplify that rule in File > Board Setup > Design Rules > Custom Rules, or leave the .kicad_dru out, and submit again.","retryable":false,"retry":"Remove or simplify the custom rule, then submit again.","message":"Your .kicad_dru uses a custom rule condition Zap! cannot apply yet (for example insideCourtyard()): remove or simplify that rule in File > Board Setup > Design Rules > Custom Rules, or leave the .kicad_dru out, and submit again.","details":{}},{"code":"output_has_shorts","http_status":null,"where":["job failure"],"job_failure":true,"meaning":"Every routing attempt left a short circuit between different nets, so no board is returned.","fix":"Attach the .kicad_pro saved with the board (File > Save) so Zap! routes to your rules, and send the job id to hello@mutual.solutions if it repeats.","retryable":false,"retry":"Send your .kicad_pro; send the job id if it repeats.","message":"Every routing attempt left a short circuit between different nets, so no board is returned: attach the .kicad_pro saved with the board (File > Save) so Zap! routes to your rules, and send the job id to hello@mutual.solutions if it repeats.","details":{}},{"code":"claim_invalid","http_status":404,"where":["POST /v1/claim"],"job_failure":false,"meaning":"This claim link is not valid.","fix":"Check that you copied the whole link, or ask mutual for a new one.","retryable":false,"retry":"Check the whole claim link.","message":"This claim link is not valid: check that you copied the whole link, or ask mutual for a new one.","details":{}},{"code":"claim_used","http_status":410,"where":["POST /v1/claim"],"job_failure":false,"meaning":"This key was already claimed and is shown only once.","fix":"Ask hello@mutual.solutions for a new key.","retryable":false,"retry":"Ask for a new key.","message":"This key was already claimed and is shown only once: ask hello@mutual.solutions for a new key.","details":{}},{"code":"claim_expired","http_status":410,"where":["POST /v1/claim"],"job_failure":false,"meaning":"This claim link has expired (links last 72 hours).","fix":"Ask hello@mutual.solutions for a new one.","retryable":false,"retry":"Ask for a new claim link.","message":"This claim link has expired (links last 72 hours): ask hello@mutual.solutions for a new one.","details":{}},{"code":"feedback_needs_user_approval","http_status":400,"where":["POST /v1/feedback"],"job_failure":false,"meaning":"Feedback is sent only with the user's approval (user_approved is not true).","fix":"Ask your user whether to send this exact text; send only if they agree.","retryable":false,"retry":"Ask your user whether to send this exact text; send it again with user_approved true only if they agree.","message":"Feedback is sent only with the user's approval (user_approved is not true): ask your user whether to send this exact text; send only if they agree.","details":{}},{"code":"feedback_message_length","http_status":400,"where":["POST /v1/feedback"],"job_failure":false,"meaning":"The feedback message is empty or longer than 4,000 characters.","fix":"Send the user's words in 1 to 4,000 characters, shortened only with their agreement.","retryable":false,"retry":"Send 1 to 4,000 characters; shorten only with your user's agreement.","message":"The feedback message is empty or longer than 4,000 characters: send the user's words in 1 to 4,000 characters, shortened only with their agreement.","details":{}},{"code":"invalid_feedback","http_status":400,"where":["POST /v1/feedback"],"job_failure":false,"meaning":"The feedback request is not valid (field names the first problem).","fix":"Send JSON with message (text), user_approved true, and optionally job_id, kind (praise, problem, idea or other) and contact_ok (true or false), snapshot_ids (at most 3 pictures of this job), and nothing else.","retryable":false,"retry":"Fix the field named in field, then send again.","message":"The feedback request is not valid (field names the first problem): send JSON with message (text), user_approved true, and optionally job_id, kind (praise, problem, idea or other) and contact_ok (true or false), snapshot_ids (at most 3 pictures of this job), and nothing else.","details":{}},{"code":"feedback_limit_reached","http_status":429,"where":["POST /v1/feedback"],"job_failure":false,"meaning":"This key has sent today's feedback messages (20 per UTC day).","fix":"Send more after 00:00 UTC, or email hello@mutual.solutions.","retryable":false,"retry":"Not today: this key's feedback messages are used up until 00:00 UTC (Retry-After gives the seconds); email hello@mutual.solutions instead.","message":"This key has sent today's feedback messages (20 per UTC day): send more after 00:00 UTC, or email hello@mutual.solutions.","details":{}},{"code":"snapshots_off","http_status":403,"where":["POST /v1/jobs/{id}/snapshot"],"job_failure":false,"meaning":"Board pictures are switched off for this key (keys a platform uses for its own customers have them off, as their boards are not the key holder's to show).","fix":"Send the feedback as text only.","retryable":false,"retry":"Not with this key: send the feedback as text only.","message":"Board pictures are switched off for this key (keys a platform uses for its own customers have them off, as their boards are not the key holder's to show): send the feedback as text only.","details":{}},{"code":"invalid_snapshot","http_status":400,"where":["POST /v1/jobs/{id}/snapshot"],"job_failure":false,"meaning":"The picture request is not valid (field names the first problem).","fix":"Send JSON with x_mm and y_mm (the area's centre in mm, as KiCad shows positions), and optionally size_mm (1 to 40), layers (copper layer names such as F.Cu) and show_issues (true or false), and nothing else.","retryable":false,"retry":"Fix the field named in field, then ask again.","message":"The picture request is not valid (field names the first problem): send JSON with x_mm and y_mm (the area's centre in mm, as KiCad shows positions), and optionally size_mm (1 to 40), layers (copper layer names such as F.Cu) and show_issues (true or false), and nothing else.","details":{}},{"code":"snapshot_too_large","http_status":400,"where":["POST /v1/jobs/{id}/snapshot"],"job_failure":false,"meaning":"The picture area is larger than 40 mm (pictures show one spot, never the whole board).","fix":"Send size_mm of at most 40, centred on the spot (the report's at_mm positions help).","retryable":false,"retry":"Send size_mm of at most 40 and aim at the spot.","message":"The picture area is larger than 40 mm (pictures show one spot, never the whole board): send size_mm of at most 40, centred on the spot (the report's at_mm positions help).","details":{}},{"code":"snapshot_outside_board","http_status":422,"where":["POST /v1/jobs/{id}/snapshot"],"job_failure":false,"meaning":"The picture area misses the board (board_mm gives the board's extent).","fix":"Aim x_mm and y_mm at a spot on the board, for example a position from the report.","retryable":false,"retry":"Aim x_mm and y_mm at a spot on the board.","message":"The picture area misses the board (board_mm gives the board's extent): aim x_mm and y_mm at a spot on the board, for example a position from the report.","details":{}},{"code":"snapshot_limit_reached","http_status":429,"where":["POST /v1/jobs/{id}/snapshot"],"job_failure":false,"meaning":"This key has made today's board pictures (30 per UTC day).","fix":"Make more after 00:00 UTC, or send the feedback as text only.","retryable":false,"retry":"Not today: this key's board pictures are used up until 00:00 UTC (Retry-After gives the seconds); send the feedback as text only.","message":"This key has made today's board pictures (30 per UTC day): make more after 00:00 UTC, or send the feedback as text only.","details":{}},{"code":"snapshot_not_found","http_status":404,"where":["GET /v1/snapshots/{id}.png","DELETE /v1/snapshots/{id}.png","POST /v1/feedback"],"job_failure":false,"meaning":"No picture with this id is available for this key and job (a picture is deleted 24 hours after it was made unless attached to feedback, and attaches to one feedback only).","fix":"Make a new one with POST /v1/jobs/{id}/snapshot.","retryable":false,"retry":"Make a new picture with POST /v1/jobs/{id}/snapshot.","message":"No picture with this id is available for this key and job (a picture is deleted 24 hours after it was made unless attached to feedback, and attaches to one feedback only): make a new one with POST /v1/jobs/{id}/snapshot.","details":{}},{"code":"tryit_limit_reached","http_status":429,"where":["POST /v1/try/jobs"],"job_failure":false,"meaning":"Your network address has used today's free trial jobs.","fix":"Try again after 00:00 UTC, or sign in at https://zap.mutual.solutions/auth/login and create an API key on your account page.","retryable":false,"retry":"Not today: this address's free trial jobs are used up until 00:00 UTC (Retry-After gives the seconds); or sign in at https://zap.mutual.solutions/auth/login and create an API key on your account page.","message":"Your network address has used today's free trial jobs: try again after 00:00 UTC, or sign in at https://zap.mutual.solutions/auth/login and create an API key on your account page.","details":{}},{"code":"tryit_board_too_large","http_status":422,"where":["POST /v1/try/jobs"],"job_failure":false,"meaning":"The board is too large for the free trial (more pads than max_pads in this answer, or a very large outline).","fix":"Try a smaller board, or sign in at https://zap.mutual.solutions/auth/login and create an API key on your account page to route this one.","retryable":false,"retry":"Not retryable: send a smaller board, or use an API key.","message":"The board is too large for the free trial (more pads than max_pads in this answer, or a very large outline): try a smaller board, or sign in at https://zap.mutual.solutions/auth/login and create an API key on your account page to route this one.","details":{}},{"code":"tryit_layers_unsupported","http_status":422,"where":["POST /v1/try/jobs"],"job_failure":false,"meaning":"The free trial routes only boards with 2 or 4 copper layers.","fix":"Try such a board, or sign in at https://zap.mutual.solutions/auth/login and create an API key on your account page to route this one.","retryable":false,"retry":"Not retryable: send a board with 2 or 4 copper layers, or use an API key.","message":"The free trial routes only boards with 2 or 4 copper layers: try such a board, or sign in at https://zap.mutual.solutions/auth/login and create an API key on your account page to route this one.","details":{}},{"code":"tryit_busy","http_status":503,"where":["POST /v1/try/jobs"],"job_failure":false,"meaning":"The free trial is busy right now (another visitor's board is routing, or today's trial jobs are used up).","fix":"Try again after the Retry-After seconds, or sign in at https://zap.mutual.solutions/auth/login and create an API key on your account page.","retryable":true,"retry":"Wait the seconds in the Retry-After header, then submit again.","message":"The free trial is busy right now (another visitor's board is routing, or today's trial jobs are used up): try again after the Retry-After seconds, or sign in at https://zap.mutual.solutions/auth/login and create an API key on your account page.","details":{}},{"code":"tryit_human_check_failed","http_status":403,"where":["POST /v1/try/jobs"],"job_failure":false,"meaning":"The human check did not pass or has expired.","fix":"Complete it again on the page, then press the button once more.","retryable":true,"retry":"Complete the human check again, then submit.","message":"The human check did not pass or has expired: complete it again on the page, then press the button once more.","details":{}},{"code":"tryit_not_found","http_status":404,"where":["GET /v1/try/jobs/{id}","GET /v1/try/jobs/{id}/result","GET /v1/try/jobs/{id}/report.json","GET /v1/try/jobs/{id}/report/{layer}.svg"],"job_failure":false,"meaning":"No free trial job matches this id and token.","fix":"The token is given only once, in the answer to the upload, so check the job from the same page.","retryable":false,"retry":"Use the id and token from the upload's answer.","message":"No free trial job matches this id and token: the token is given only once, in the answer to the upload, so check the job from the same page.","details":{}},{"code":"invalid_access_request","http_status":422,"where":["POST /v1/access-requests"],"job_failure":false,"meaning":"The request is incomplete or not valid (field names the first problem).","fix":"Give a valid email address of at most 254 characters, a use case of 1 to 2,000 characters and at most 200 characters of name or company, tick the consent box, then send it again.","retryable":false,"retry":"Fix the field named in field, then send again.","message":"The request is incomplete or not valid (field names the first problem): give a valid email address of at most 254 characters, a use case of 1 to 2,000 characters and at most 200 characters of name or company, tick the consent box, then send it again.","details":{}},{"code":"access_requests_closed","http_status":503,"where":["POST /v1/access-requests"],"job_failure":false,"meaning":"We are not taking more access requests through this form right now.","fix":"Email hello@mutual.solutions instead.","retryable":false,"retry":"Email hello@mutual.solutions instead.","message":"We are not taking more access requests through this form right now: email hello@mutual.solutions instead.","details":{}}],"check_findings":[{"code":"very_large_board","severity":"warning","message":"The board outline is very large for its copper layers, so the job may hit the memory limit and fail with timeout_or_limit: if it does, split the panel into single boards in KiCad and route them one by one.","meaning":"The board outline is very large for its copper layers, so the job may hit the memory limit and fail with timeout_or_limit.","fix":"If it does, split the panel into single boards in KiCad and route them one by one."},{"code":"rules_finer_than_jlc","severity":"warning","message":"Your net classes ask for finer copper than JLC can make and fab_floor is jlc, so some nets may stay unrouted: use fab_floor=board if your fab supports it (sizes: File > Board Setup > Design Rules > Net Classes).","meaning":"Your net classes ask for finer copper than JLC can make and fab_floor is jlc, so some nets may stay unrouted.","fix":"Use fab_floor=board if your fab supports it (sizes: File > Board Setup > Design Rules > Net Classes)."},{"code":"courtyards_overlap","severity":"warning","message":"Some footprint courtyards overlap: Zap! keeps your placement, so these parts may end up too close to route or assemble; move them apart (Inspect > Design Rules Checker lists them).","meaning":"Some footprint courtyards overlap.","fix":"Zap! keeps your placement, so these parts may end up too close to route or assemble; move them apart (Inspect > Design Rules Checker lists them)."},{"code":"malformed_courtyard","severity":"warning","message":"Some footprints have a broken courtyard outline: fix the footprint's F.Courtyard / B.Courtyard drawing in the Footprint Editor (Tools > Load Footprint from Board).","meaning":"Some footprints have a broken courtyard outline.","fix":"Fix the footprint's F.Courtyard / B.Courtyard drawing in the Footprint Editor (Tools > Load Footprint from Board)."},{"code":"no_project","severity":"warning","message":"No .kicad_pro was sent: Zap! routes and checks to KiCad's default rules (0.2 mm clearance and track width); attach the .kicad_pro next to your board (File > Save writes it) to use your own net classes and rules.","meaning":"No .kicad_pro was sent.","fix":"Zap! routes and checks to KiCad's default rules (0.2 mm clearance and track width); attach the .kicad_pro next to your board (File > Save writes it) to use your own net classes and rules."},{"code":"unknown_net_class","severity":"warning","message":"Your .kicad_pro assigns nets to net classes it does not define, so those nets fall back to the Default class: define them in File > Board Setup > Design Rules > Net Classes.","meaning":"Your .kicad_pro assigns nets to net classes it does not define, so those nets fall back to the Default class.","fix":"Define them in File > Board Setup > Design Rules > Net Classes."},{"code":"footprints_outside_board","severity":"warning","message":"Some footprints are outside the board outline (listed in footprints, with positions): Zap! routes the rest of the board and leaves their pads unconnected (they appear in the report's unconnected list); if they belong on the board, move them onto it in KiCad (select the footprint and press M), save and check again.","meaning":"Some footprints are outside the board outline (listed in footprints, with positions).","fix":"Zap! routes the rest of the board and leaves their pads unconnected (they appear in the report's unconnected list); if they belong on the board, move them onto it in KiCad (select the footprint and press M), save and check again."},{"code":"existing_pours_replaced","severity":"warning","message":"Zap! removes every copper pour on the board and makes its own ground and supply pours, choosing their nets itself; pours lists each one that will be replaced (net and layers): compare the routed board's pours with your design, or send keep_existing=true to keep your pours, tracks and vias exactly.","meaning":"Zap! removes every copper pour on the board and makes its own ground and supply pours, choosing their nets itself; pours lists each one that will be replaced (net and layers).","fix":"Compare the routed board's pours with your design, or send keep_existing=true to keep your pours, tracks and vias exactly."},{"code":"check_incomplete","severity":"warning","message":"Part of the check could not run, so some problems may only show when the job runs: run Inspect > Design Rules Checker in KiCad yourself, or check again in a minute.","meaning":"Part of the check could not run, so some problems may only show when the job runs.","fix":"Run Inspect > Design Rules Checker in KiCad yourself, or check again in a minute."}],"network_edge":[{"http_status":[403],"body_contains":"error code: 1010","meaning":"Cloudflare, the network edge in front of Zap!, blocked the client's name (User-Agent); Python's built-in urllib sends a default name that is blocked.","fix":"Send any User-Agent header, e.g. 'my-agent/1.0', and repeat.","retryable":true},{"http_status":[502,503,504],"body_contains":"<html","meaning":"Short maintenance at the network edge (usually under a minute).","fix":"Wait 30 to 60 s; repeat GET requests; for POST /v1/jobs list your jobs first.","retryable":true}]}