Using openapi.json
Generate a client, import it into ChatGPT Actions, Postman or an agent.
Where it is
The REST API is described in OpenAPI 3.1 at /openapi.json. It is generated from the operation catalogue every time it is served, so it is always current: every catalogue operation with its input schema, and the resource routes under /api/v1. Its servers entry is this deployment.
Authentication
One security scheme, bearerAuth (HTTP bearer): an organisation API key as Authorization: Bearer shc_… on every request, never in a URL. The key's scope decides which operations answer 403; see API keys and scopes.
Generating a client
openapi-typescript turns the document into types; openapi-fetch is a typed client over them.
ChatGPT custom GPT Actions
- Create a GPT → Configure → Actions → Create new action → Import from URL, and paste the document's URL above.
- Authentication: API Key, Auth Type Bearer, and paste a key. Give it the narrowest scope the GPT needs:
fleet:readto look,fleet:runto answer agents and replay. - The GPT then acts as that key, within its organisation and scope, and every call is in the audit log under the key's name.
Postman
File → Import → paste the document's URL. On the imported collection, set Authorization to Bearer Token with the key, and every request inherits it. Keep the key as a collection variable marked secret rather than in a request.
Reading the catalogue at run time
A client that builds itself when it runs, the way skillhook's CLI and MCP server do, reads the catalogue instead of the OpenAPI document: one request, every operation with its JSON Schema (draft 2020-12, derived the way the MCP SDK derives a tool's), the scope it needs, whether this key may call it, and the instructions for agents. The shape is versioned (version: 1): it changes only when an older client would misread it.
The error contract
Every refusal is an RFC 9457 problem: application/problem+json with type (a link to the matching anchor of the API reference), title, status, code, detail and request_id. Credentials are never echoed. Over MCP the same refusals are tool results with isError and the message.
Request ids
Every answer carries an x-request-id header, and every problem the same id in its body. Send your own with an X-Request-Id header (letters, digits, ., _, : and -, up to 128 characters) and it is used instead; otherwise one is generated. Quote it when you report a problem (report_issue, or Support on the dashboard): it is what the logs on our side are keyed by.