---
title: Handling k12panel API errors
description: Every k12panel API error response uses the same shape, so your program can handle them consistently.
---

[Skip to content](https://k12panel.com/kb/developer-api/handling-k12panel-api-errors#main-content)

![test-full-word-logo.png\]](https://k12panel.com/hs-fs/hubfs/test-full-word-logo.png?height=35&name=test-full-word-logo.png)

Open main navigation

Close main navigation

- [Go to k12panel.com](https://k12panel.com)

[Go to k12panel.com](https://k12panel.com?hsLang=en)

 Hello. How can we help you?

- There are no suggestions because the search field is empty.

1. [Online Help and Knowledge Base](https://k12panel.com/kb?hsLang=en)
2. [Developer API](https://k12panel.com/kb/developer-api?hsLang=en)

# Handling k12panel API errors

## Every error response uses the same shape, so your program can handle them consistently.

Every error response uses the same shape, so your program can handle them consistently:

```
{ "error": "not_found", "message": "No person matches email 'nobody@school.org'." } 
```

Check the HTTP status code and the `error` field.

#### Common responses

| Status | `error` | Meaning | What to do |
| --- | --- | --- | --- |
| 401 | `authentication_failed` | Missing, invalid, revoked, or expired key | Check the `Authorization` header; rotate the key if needed |
| 403 | `insufficient_scope` | The key lacks the required permission | Grant the permission (or use a key that has it) |
| 404 | — | The record doesn't exist in your organization | Check the id; you can only access your own org's data |
| 409 | `already_checked_out` | The asset is already checked out to someone | Check it in first, or leave it as-is |
| 409 | `ambiguous_reference` | A lookup matched more than one record | Retry with an exact `id` (see below) |
| 409 | `serial_conflict` | A create/edit would reuse a serial already in your org | Use the existing device returned in `candidates` |
| 409 | `email_conflict` | A person create/edit would reuse an email already in your org | Use the existing person returned in `candidates` |
| 409 | `person_sync_blocked` | Can't delete a person synced from a cloud service | Remove them in that service first |
| 409 | `person_has_checkouts` | Can't delete a person with assets still checked out | Check their devices in first |
| 422 | `group_is_dynamic` | Can't hand-edit membership of a rule-managed group | Change the attribute the group's rule matches on |
| 422 | `device_cannot_receive_commands` | The device has no agent, an agent older than 0.2, or hasn't reported an encryption key | Read the message; agentless devices (Chromebooks, MDM-only) can't take commands |
| 422 | `asset_state_not_commandable` | The device is archived, trashed, or awaiting enrollment | Only active and inactive devices accept commands |
| 409 | `library_command_changed` | A pinned library command was edited since you reviewed it | Re-review the script, then re-pin `expect_revision` to `current_revision` |
| 400 | `idempotency_key_required` | A create that can't be safely retried was sent without one | Send an `Idempotency-Key` header (required for reboot/shutdown/restart agent, and for serial-less or email-less creates) |
| 413 | `batch_too_large` | A bulk request exceeded the 1,000-item limit | Split it into smaller batches |
| 422 | `not_found` | A referenced record (person/site) wasn't found | Check the email/name you sent |
| 400 | `unknown_include` | An unrecognised block name in `include=` | Use `health`, `security`, or `inventory` |
| 400 | `invalid_datetime` | A date filter couldn't be read | Send `2026-07-18` or `2026-07-18T14:30:00Z` |
| 400 | `invalid_buckets` | `buckets=` wasn't ascending, positive day thresholds | Send e.g. `buckets=1,7,30` |
| 400 | `expand_requires_name` | `expand=assets` on the software catalog without `name` | Add `name=` to pick a title first |
| 422 | `invalid` | The request body failed validation | See the `details` field |
| 429 | — | Too many requests | Wait a moment and retry |

#### The "more than one match" case

Because emails, serials, and names aren't always unique, a lookup used in an action (like checkout) can match more than one record. Instead of guessing, the API returns the **list of candidates** so you can pick the right one and retry with its `id`:

```
{  "error": "ambiguous_reference",  "message": "3 people match email 'jsmith@school.org'. Retry with an id.",  "candidates": [    { "id": 3087, "first_name": "John",  "last_name": "Smith", "org_unit": "HS / Staff",    "groups": ["Teachers"] },    { "id": 4192, "first_name": "Jane",  "last_name": "Smith", "org_unit": "MS / Staff",    "groups": ["Teachers"] },    { "id": 5310, "first_name": "Jamie", "last_name": "Smith", "org_unit": "HS / Students", "groups": ["Grade 11"] }  ]}
```

The easy case (a unique email) is a single call. The ambiguous case gives you everything you need to resolve it — including enough detail to show a technician a "which one?" prompt. Retry the action with the exact `id`:

```
{ "person": { "id": 3087 } } 
```

#### Rate limits

Each key can make a reasonable number of requests per minute. If you exceed it you'll get a `429` response — pace your requests and retry after a moment. Well-behaved tools won't hit this in normal use.

- [Getting started](https://k12panel.com/kb/getting-started?hsLang=en)
- [Menu Functions](https://k12panel.com/kb/menu-functions?hsLang=en)
- [Settings](https://k12panel.com/kb/settings?hsLang=en)
- [3rd Party Integrations](https://k12panel.com/kb/3rd-party-integrations?hsLang=en)
- [PC Health and SOAR](https://k12panel.com/kb/pc-health-and-soar?hsLang=en)
- [FAQ](https://k12panel.com/kb/faq?hsLang=en)
- [K12Panel Agent](https://k12panel.com/kb/k12panel-agent?hsLang=en)
- [Blueprints Catalog](https://k12panel.com/kb/blueprints-catalog?hsLang=en)
- [Writing your own Blueprint](https://k12panel.com/kb/writing-your-own-blueprint?hsLang=en)
- [MSP Tools](https://k12panel.com/kb/msp-tools?hsLang=en)
- [Developer API](https://k12panel.com/kb/developer-api?hsLang=en)

# K12Panel Inc

k12panel.com Help Center

Copyright © 2026, K12Panel Inc