Parselv1.0.0

Errors

Status codes the Parsel API returns and how to handle them.

The Parsel API uses standard HTTP status codes. All error responses share one body shape, so client code can branch on errors.status regardless of which endpoint produced the error.

Status codes

StatusMeaningWhen you'll see it
200 OKSuccessThe request was authenticated, the resource exists, and the body contains the data.
400 Bad RequestValidation errorThe request body or parameters failed validation. Returned by endpoints that accept input.
401 UnauthorizedMissing or invalid tokenThe Authorization header is absent, malformed, or carries a token that has been revoked or expired. Applies to every authenticated endpoint.
404 Not FoundResource not visibleThe resource ID does not exist, or it exists but belongs to a different account. The two cases are not distinguished — both return 404.
422 Unprocessable EntityBusiness-rule rejectionThe request was syntactically valid but rejected by a business rule (for example, an invoice in the wrong state for the requested action).

The Billing read endpoints (GET /billing/invoices, GET /billing/invoices/{id}, GET /billing/invoices/{id}/line_items) return only 200, 401, and 404. Endpoints that accept request bodies may additionally return 400 or 422.

Error body shape

All non-2xx responses share this shape:

{
  "errors": {
    "message": "The requested resource could not be found",
    "status": "Not Found"
  }
}

Branch on errors.status for programmatic handling, or display errors.message to a human.

401 responses are produced by the auth middleware before reaching any controller. Treat any 401 as "stop and refresh credentials" — see Authentication.

Suggested handling

async function callParsel(url: string, init?: RequestInit) {
  const res = await fetch(url, {
    ...init,
    headers: {
      Authorization: `Bearer ${process.env.PARSEL_TOKEN}`,
      ...(init?.headers ?? {}),
    },
  });

  switch (res.status) {
    case 200:
      return res.json();
    case 401:
      throw new Error("Parsel token rejected — refresh credentials");
    case 404:
      return null; // not found, or not yours — same response either way
    case 400:
    case 422: {
      const body = await res.json();
      throw new Error(`Parsel rejected request: ${body.errors?.message}`);
    }
    default:
      throw new Error(`Unexpected Parsel response: ${res.status}`);
  }
}
def call_parsel(url, **kwargs):
    resp = requests.get(
        url,
        headers={"Authorization": f"Bearer {os.environ['PARSEL_TOKEN']}"},
        **kwargs,
    )
    if resp.status_code == 200:
        return resp.json()
    if resp.status_code == 401:
        raise RuntimeError("Parsel token rejected — refresh credentials")
    if resp.status_code == 404:
        return None
    if resp.status_code in (400, 422):
        body = resp.json()
        raise RuntimeError(f"Parsel rejected request: {body.get('errors', {}).get('message')}")
    resp.raise_for_status()

Treat 404 on a known-valid resource ID as "you don't own this resource" rather than "the resource was deleted." Endpoints scope access to the authenticated account.

On this page