Errors

Every refusal is a JSON body with one key:

{ "error": "Could not find that document." }

The words are meant to be read. The status is what to branch on.

Status Meaning
202 Asked for, and not finished — a solve that is still running
400 A parameter this endpoint does not accept, or one sent where it does not apply
401 No token, or one that has been revoked
403 The token is for a different team, or is missing the scope this needs
404 No such thing, or nothing you can see — the two are deliberately the same answer
409 The thing exists but is not in a state that can answer yet — or, for a plug-in upload, collides with something the team already has
413 A plug-in upload over 64 MB
422 A plug-in upload that is not a Grasshopper plug-in NodePen can load
500 Something is wrong on our side, not with the request
502 A solve failed. The document is fine; the script did not run

404 says less than it looks like

A document that is not there and a document that is not yours are the same answer, in the same words. That is deliberate: telling them apart would let anyone holding a token enumerate what other teams own by watching which ids answer differently. Folders and plug-ins behave the same way.

Changing a plug-in goes one step further: it is a team admin's, so a token belonging to anyone else is answered Could not find that team., as though the team were not there.

So a 404 means "this id is not something you can read", and no more than that.

Almost nothing here is gated by plan

No endpoint that reads refuses for billing reasons. Versions, solutions and exports are the product rather than a tier of it, so a token that can reach a document can reach everything this API says about it. A refusal is always about the token, the id, or the state of the thing — never about what the team pays.

The exception is changing plug-ins. Uploading, enabling, disabling and removing one needs a team on the Team or Custom plan, or one NodePen has enabled custom plug-ins for, and is refused 403 with Custom plug-ins are part of the Team plan. anywhere else. Listing them is not: a team that no longer has custom plug-ins still solves with the ones it has.

What a call can still cost is credits: solving is what spends them, which is why no GET here starts a solve. See 409 below.

409 is usually about solving

POST /api/v1/plugins/uploads/{uploadId} answers 409 for a plug-in that collides with something, or one whose file has not all arrived — see its reference. Otherwise two endpoints answer 409, and both mean the same thing: the document exists and you can read it, but there is no solution behind it yet.

  • GET /api/v1/documents/{documentId}/solution — nothing has ever been solved.
  • GET /api/v1/documents/{documentId}/export — an export is of a solution, so a document nobody has run has nothing to write out.

That is a very ordinary state for a document just created or just imported. Opening it in NodePen solves it. The API deliberately will not, here or anywhere: solving costs your team credits, and a GET that quietly spent them would be a surprising way to find that out.

The three ways a solution can decline

/solution distinguishes them so you do not have to inspect the body to know what to do:

Status Meaning What to do
409 This document has never been solved Open it in the editor
202 A solve is running Try the same request shortly
502 The most recent solve failed Nothing, until the script is fixed

400 is only ever a parameter

Five cases, all on the two endpoints that take one:

  • A format this endpoint does not answer in. /solution takes json or bimz, with no leading dot. /export takes .gh or .3dm, with one. They are different because one names representations of a solve and the other names file extensions.
  • A rhinoVersion that is not 7, 8 or 9.
  • A rhinoVersion sent alongside format=.gh. It only applies to a .3dm export, and is refused rather than ignored — a parameter that silently does nothing is discovered much later, in Rhino.
  • A geometryType that is not solution or display.
  • A geometryType sent alongside format=.gh, refused for the same reason.

500 means a broken row, not a broken request

This team is not set up correctly. means a team is missing the root folder that is written in the same transaction as the team itself. There is nothing a caller can do about it; tell us.