/api/v1 on your Command Center host is the stable, documented HTTP API: what you script against to drive 2501 from a pipeline, a CMDB sync, or a shell script. It is a real contract, not a private back end that happens to be reachable - Command Center’s own hosts and agents screens are built on these same endpoints.
200 is how you confirm a key works. What the key can reach is fixed at creation - see Authentication.
What is in the API
Every endpoint - with its request and response schema and a live request builder - is in the API Reference in the sidebar.
Other things Command Center manages - credentials, specialties, operational rules, tickets, jobs, knowledge - are still served from unversioned routes that exist for the UI and are not part of the
v1 contract yet. They can change in any release. Resources move under /api/v1 release by release, and only what is documented here is stable.Authentication
Two ways in, and every/api/v1 endpoint accepts either:
- An API key as a bearer token. This is the one for scripts and integrations.
- A Command Center session cookie. This is what the web UI uses.
- A key is an administrator inside its own scope. Full read and write on its organization, or on every organization in the tenant for a tenant-scoped key. That reach is frozen at creation: it does not follow the person who created it.
- A key never reaches anything outside
/api/v1. The bearer header is ignored elsewhere, and the CC-only/api/internalroutes reject it outright. Notably, a key cannot create, list, or revoke keys - that stays session-only, so a leaked key cannot mint a successor. - A bad key is a hard failure, never a downgrade. An invalid, revoked, or expired key gets
401 UNAUTHORIZED; it never falls back to an anonymous request.
Organizations
Hosts and agents belong to exactly one organization, so anything addressing a collection has to name one:- reading a collection (
GET /hosts,/hosts/search,/hosts/export) carriesorg_idin the query string, - creating (
POST /hosts) carriesorg_idin the body, or in the query string for a CSVbatch, - anything addressed by id - a read, an update, a delete, an action, a sub-resource - carries none. The id already fixes the organization.
400. Naming one you cannot reach fails in one of two ways, and the difference is deliberate: an org-scoped caller pointing anywhere outside its own organization gets 403 ORG_ACCESS_DENIED, while a tenant-scoped caller naming an organization that is not in its tenant gets 404 NOT_FOUND - another tenant’s organizations are invisible, not merely forbidden.
Reading lists
Every list endpoint answers with the same envelope:limit rows, then pass the id of the last row you got as starting_after to get the next page. has_more tells you when to stop.
page parameter anywhere in v1. A cursor id that does not resolve - a deleted row, a row in another organization - is a 400 INVALID_CURSOR rather than a silent jump back to page one.
Writing
POST creates and POST updates. POST /api/v1/hosts creates a host; POST /api/v1/hosts/{id} updates that one. There is no PUT in v1.
An update is a partial update, and the rules are the same everywhere:
POST actions on an item: POST /api/v1/agents/{id}/archive, POST /api/v1/agents/{id}/test-connection.
Success is always 200, creates included. There is no 201 and no 204.
Errors
Every error, on every endpoint, has the same flat body:
The codes you will actually meet:
code is drawn from a closed set, so it is safe to branch on. The table above is the part of that set a normal caller meets; a few others (TIMEOUT, PAYLOAD_TOO_LARGE, RATE_LIMITED) exist for the same reasons they do in any HTTP API. Treat an unrecognised code by its status class.
Rate limiting
v1 sets no rate limit of its own, but Command Center applies a per-deployment one in front of every route, and a request over it comes back 429 RATE_LIMITED. The default allows a few hundred requests a minute per client, which no ordinary integration approaches - it is there to stop a runaway loop, not to meter you. Because an operator can change it, treat the exact number as a property of the deployment you are calling, not of the API: back off on a 429 rather than pacing yourself to a constant.
Two things do have fixed limits, and they are the ones a bulk caller hits first: a list page is capped at 100 rows (limit), and import and export are capped at 10000 rows.
Bulk import is the one deliberate exception: POST /{resource}/batch returns 200 with a verdict per row even when every row failed, because the per-row results are the answer. A 4xx there means the request itself was wrong.
Versioning
v1 is a promise about shapes, not a frozen file. Fields and optional parameters get added; nothing documented here is renamed, removed, or given a new meaning without a new version. So:
- read fields by name and ignore the ones you do not know,
- do not depend on field order or on the absence of a field,
- do not depend on undocumented fields. A response may carry extras the UI needs; those are not part of the contract,
- treat a documented enum as open unless it is stated closed. Host
tagsandtarget_typeare closed sets; a task’sstatusis a state machine that gains steps.
v2 would be a new path served beside v1. Watch the release notes for the release you upgrade to.
Next
API Keys
Generating, scoping, and revoking keys.
Import and Export
Bulk CSV and JSON, in and out.

