API reference
This is the public API: calling a tool over REST or MCP, reading your workspace over the workspace MCP, and the webhooks you can be sent. It's drawn from the same description the API serves at /api/v1/openapi.json, so the two always agree. A tool's exact search schema is its own, built from what its owner lets callers filter, exclude and sort: read it from the tool's schema endpoint.
Base URL https://cloudcrane.in/api. Every call sends its key as Authorization: Bearer <key>: a tool key (cc_live_…) for a tool, a build key (cc_build_…) for the workspace MCP. Errors come back as { "error": "…" }, with a detail when a request didn't match.
The same description, for code generators and agents: https://cloudcrane.in/api/v1/openapi.json.
/v1/tools/{name}/schematool keyWhat the tool is and what it takes
The tool's name and description for an agent, the release it serves, how that release measured on its answer key (when it has one), and the JSON Schema of the search request: which filters, exclusions and sorts this tool allows. Doesn't count towards the key's quota.
Parameters
| name* | string | path. The tool's name, as its owner set it. |
Responses
- 200The tool ·
ToolDescription - 401No key, or a key that is revoked or expired. Send it as
Authorization: Bearer <key>. ·Error - 403A browser origin the key doesn't allow. Call from your server, or add the origin to the key. ·
Error - 404No tool with that name for this key. ·
Error - 429The key's rate limit or monthly quota.
Retry-Aftersays when to try again after a rate limit. ·Error - 503The tool is paused by its owner, or has no published data yet. ·
Error
/v1/tools/{name}/searchtool keySearch the tool's records
Words, a record to find others like, filters, safety exclusions and sorting, as the tool's schema allows. Exclusions are enforced by the server: a record comes back only if it is known not to have an excluded value, so an unknown value is never treated as safe. Exclusions pinned to the key are always applied as well; a request can only narrow them.
Parameters
| name* | string | path. The tool's name, as its owner set it. |
Body
| query | string | Words to search for.up to 200 chars |
| similar_to | string (uuid) | A record's id; returns records like it instead. Not with query. |
| filters | object | Only records matching every given filter. |
| exclude | object | Safety field → values to leave out. A record is returned only if it is known not to have any of them. |
| sort | object[] | |
| ↳ field* | string | |
| ↳ direction | "asc" | "desc" | |
| limit | integer | Results per page, up to the tool's maximum.min 1 |
| cursor | string | next_cursor from the previous page, with the same other parameters. |
| include_receipts | boolean | default true |
Responses
- 200A page of results ·
SearchResult - 400The request doesn't match this tool's schema;
detailsays where. ·Error - 401No key, or a key that is revoked or expired. Send it as
Authorization: Bearer <key>. ·Error - 403A browser origin the key doesn't allow. Call from your server, or add the origin to the key. ·
Error - 404No tool with that name for this key. ·
Error - 429The key's rate limit or monthly quota.
Retry-Aftersays when to try again after a rate limit. ·Error - 503The tool is paused by its owner, or has no published data yet. ·
Error
/v1/tools/{name}/valuestool keyLook up value set entries to filter or exclude by
For a field choosing from a large value set: entries matching some words, by name, synonym or code, so their codes can go in filters and exclusions.
Parameters
| name* | string | path. The tool's name, as its owner set it. |
Body
| field* | string | A value set field the tool lets you filter or exclude by. |
| query* | string | up to 200 chars |
| limit | integer | min 1, max 25, default 10 |
Responses
- 200Matching entries
- 400The request doesn't match;
detailsays where. ·Error - 401No key, or a key that is revoked or expired. Send it as
Authorization: Bearer <key>. ·Error - 403A browser origin the key doesn't allow. Call from your server, or add the origin to the key. ·
Error - 404No tool with that name for this key. ·
Error - 429The key's rate limit or monthly quota.
Retry-Aftersays when to try again after a rate limit. ·Error - 503The tool is paused by its owner, or has no published data yet. ·
Error
/v1/tools/{name}/items/{recordId}tool keyOne record by id
A record a search returned, under the same safety exclusions the key applies. One the key's exclusions leave out looks the same as one that doesn't exist.
Parameters
| name* | string | path. The tool's name, as its owner set it. |
| recordId* | string (uuid) | path. |
| include_receipts | boolean | query.default true |
Responses
- 200The record ·
Result - 400The id isn't valid. ·
Error - 401No key, or a key that is revoked or expired. Send it as
Authorization: Bearer <key>. ·Error - 403A browser origin the key doesn't allow. Call from your server, or add the origin to the key. ·
Error - 404No tool with that name for this key. ·
Error - 429The key's rate limit or monthly quota.
Retry-Aftersays when to try again after a rate limit. ·Error - 503The tool is paused by its owner, or has no published data yet. ·
Error
/mcp/{name}tool keyThe tool over MCP
JSON-RPC over Streamable HTTP, with the tool key as a bearer token. tools/list offers search_<name> (the same request as REST search), get_<name>, and find_values when the tool has value set fields. Answers go through the same code as REST, so validation, the safety guarantee and logging are identical. GET and DELETE return 405: there are no sessions.
Parameters
| name* | string | path. The tool's name, as its owner set it. |
Body: A JSON-RPC 2.0 request, or a batch.
Responses
- 200The JSON-RPC response.
- 401No key, or a key that is revoked or expired. Send it as
Authorization: Bearer <key>. ·Error - 403A browser origin the key doesn't allow. Call from your server, or add the origin to the key. ·
Error - 404No tool with that name for this key. ·
Error - 429The key's rate limit or monthly quota.
Retry-Aftersays when to try again after a rate limit. ·Error - 503The tool is paused by its owner, or has no published data yet. ·
Error
/build/mcpbuild keyRead, and build in, the workspace you're building
For your own agent while you build: with a build key (cc_build_…) as a bearer token, it can list datasets and their columns, contracts, readiness, the review queue, receipts, value sets and runs. A key made to build can also create datasets, value sets and fields, edit fields, start runs, and publish when the accuracy gate passes by itself, through the dashboard's checks. Resolving review items, editing stored values, withholding, deleting and publishing past the gate stay with a person.
Body: A JSON-RPC 2.0 request, or a batch.
Responses
- 200The JSON-RPC response.
- 401No build key, or one that is revoked or expired. ·
Error - 403Called from a browser: a build key opens the whole workspace, so it only works from a server or an agent's runtime. ·
Error - 429The build key's rate limit or monthly quota.
Retry-Aftersays when to try again after a rate limit. ·Error
A result
What each search result, and a single record, looks like.
Result
| id* | string | |
| fields* | object | The fields the tool returns, typed: a number is a number. |
| origin* | object | |
| ↳ kind | "imported" | "generated" | |
| ↳ model | string | For a generated record, the model that made it. |
| unknown_safety_fields | string[] | Safety fields nobody has decided on this record. |
| receipts | object |
Webhooks
A POST to your own address when something happens. Every delivery carries the event in CloudCrane-Event, its id in CloudCrane-Delivery (the same on every retry, so you can ignore repeats) and a signature in CloudCrane-Signature. Check the signature before trusting a delivery:
import { createHmac, timingSafeEqual } from "node:crypto";
// header: the CloudCrane-Signature header; body: the raw request body, as received.
export function verify(secret, body, header) {
const parts = Object.fromEntries(header.split(",").map((p) => p.trim().split("=")));
const t = Number(parts.t);
if (!Number.isInteger(t) || Math.abs(Date.now() / 1000 - t) > 300) return false;
const expected = createHmac("sha256", secret).update(`${t}.${body}`).digest();
const given = Buffer.from(parts.v1 ?? "", "hex");
return given.length === expected.length && timingSafeEqual(given, expected);
}release.readyA new release finished building
data
| dataset_id | string | |
| release_id | string | |
| release | integer | The release's number. |
release.heldA tool's scenarios held a release back
data
| tool_id | string | |
| tool | string | |
| release | integer | |
| failed | object[] | |
| ↳ scenario | string | |
| ↳ problems | string[] |
drift.alertA sync found drift and is waiting for a person
data
| dataset_id | string | |
| sync_id | string | |
| alerts | object[] | |
| ↳ field | string | |
| ↳ kind | string | |
| ↳ value | string | null | |
| ↳ severity | string | |
| ↳ message | string |
review.openedA run left decisions for a person
data
| run_id | string | |
| dataset_id | string | |
| contract_id | string | |
| count | integer | How many decisions it left. |
tool.failingA fifth or more of a tool's calls failed over 15 minutes; at most once an hour per tool
data
| tool_id | string | |
| tool | string | |
| calls | integer | |
| errors | integer | |
| window_minutes | integer | |
| dedupe_key | string | The same for every alert about the same cause. |
accuracy.droppedMeasuring the saved fields found a safety value less often than in the last release; once a day per drop
data
| dataset_id | string | |
| dataset | string | |
| evaluation_id | string | |
| regressions | object[] | |
| ↳ field | string | |
| ↳ value | string | |
| ↳ before | object | |
| ↳ after | object | |
| dedupe_key | string |
credits.lowA fifth of the starter credits left, and again when none are
data
| credits | integer | |
| used | integer | |
| left | integer |
pingA test delivery, sent from the webhook's settings
data
| message | string |