Calling conventions
The call primitive is the workhorse on every surface. It substitutes path
placeholders, builds the query string, attaches Cloudpath authentication, and
returns the response. This page covers the conventions that apply to every
call.
The return shape
Section titled “The return shape”call always resolves to the same object:
{ ok, status, statusText, headers, body, durationMs, url, retried }async () => { const r = await core.call({ method: 'GET', path: '/authServers' }); if (!r.ok) return { error: r.status, body: r.body }; return { total: r.body?.page?.totalCount };}Path parameters
Section titled “Path parameters”Path templates use {paramName} placeholders. Pass values via pathParams:
await core.call({ method: 'GET', path: '/dpskPools/{guid}/dpsks', pathParams: { guid: 'pool-guid-here' },});Pagination
Section titled “Pagination”Cloudpath list responses use a pagination envelope:
{ page: { page, pageSize, totalCount }, contents: [ /* ... */ ] }Request a page with ?page=N via the query parameter. pageSize is
server-controlled (up to 1000 records), so read it back from the response
rather than setting it.
async () => { const all = []; for (let page = 1; page <= 10; page++) { const r = await core.call({ method: 'GET', path: '/registrationLists', query: { page } }); if (!r.ok) return { error: r.status, body: r.body }; const contents = r.body?.contents ?? []; all.push(...contents); const info = r.body?.page ?? {}; const pageSize = info.pageSize ?? contents.length; const totalPages = pageSize > 0 ? Math.ceil((info.totalCount ?? 0) / pageSize) : 1; if (page >= totalPages || contents.length === 0) break; } return { count: all.length };}Filtering and sorting
Section titled “Filtering and sorting”Both are query-string conventions on Cloudpath list endpoints:
| Syntax | Notes | |
|---|---|---|
| Filter | ?filter=prop(op:val),prop2(op:val2) | Operators: like, eq, in. For in, the value is underscore-separated: val1_val2_val3 |
| Sort | ?orderBy=prop:desc,prop2:asc | Comma-separated prop:direction pairs |
Filtering server-side is worth the effort on large lists: it costs one request instead of a pagination loop, and a loop is the usual way to hit Cloudpath’s rate limit or the 20-second budget.
Nested resources hide their parent GUID
Section titled “Nested resources hide their parent GUID”Some Cloudpath responses — DPSK entries are the common case — don’t repeat the
parent’s GUID as a field. It appears only inside the entry’s links[].href
URL. If you need the pool GUID for a follow-up call, parse it out of that URL
rather than expecting a poolGuid property.
Rate limits
Section titled “Rate limits”Cloud-hosted Cloudpath enforces its own limits, independent of anything CP-MCP does:
| Limit | Value |
|---|---|
| API requests | 180 per minute, per tenant |
| Token mints | 10 per minute |
The token cache keeps CP-MCP well clear of the mint cap — a normal session mints once per surface every few minutes. The request cap is the one a wide-ranging program can reach, so prefer filters over pagination loops. See Limits and quotas.
Authentication and the automatic retry
Section titled “Authentication and the automatic retry”Authentication is handled for you. CP-MCP mints a short-lived Cloudpath JWT
from your saved credentials, caches it, and attaches it to every request as the
raw Authorization header (Cloudpath does not accept a Bearer prefix).
If Cloudpath returns 401 or 406, CP-MCP mints a fresh token once and retries
the call exactly one time. The retried field in the response is 1 when that
happened. Persistent auth failures surface to the model as ok: false. See the
full lifecycle in Security model.
Failures before Cloudpath is reached — no saved credentials, an unroutable
FQDN, a malformed request — also come back as a resolved result, but with
status: 0 and statusText: "cp_auth_error", and the reason in body.error.
Treat status: 0 as “the call never left CP-MCP”. The codes are listed in
Troubleshooting.
Sending a body
Section titled “Sending a body”For JSON, pass a plain object/array/string/number/boolean as body. For
binary uploads (multipart image uploads), you must base64-encode the raw
request bytes and pass them as bodyBase64 instead — the isolate marshals tool
arguments through JSON.stringify, which silently destroys Uint8Array,
FormData, and Blob. Passing both body and bodyBase64 returns an
invalid_body error. See
Generating portal images for the
worked multipart pattern.
Both body and bodyBase64 are ignored on GET and DELETE — if a Cloudpath
endpoint needs data on one of those verbs, put it in query.