Skip to content
Reference

API

Every action in Frazer is an API method first. The console, the CLI, the MCP server and the Terraform provider call the same handlers, so permissions, plans, quotas and audit records are the same whichever you use. The API is generated from protobuf contracts that follow the API Improvement Proposals (AIP), and the REST, gRPC and Connect surfaces, the CLI, the MCP tools, the provider and the SDKs all come from those contracts.

Host Serves
https://api.frazer.ai gRPC, Connect and gRPC-Web, and REST under /<service>/v1/…
https://mcp.frazer.ai The MCP server for AI agents
<service>.<region>.frazer.ai Regional data planes, such as registry.us-virginia1.frazer.ai for Registry

Each product is a service with a one-word ID, for example resourcemanager (organizations and projects), iam, approvals, kubernetes, serverless, databases, keyvalue (the in-memory store), storage, registry, secrets, network, dns, monitoring, ai, llm, agents, devboxes, runners, billing and quota. A service’s REST paths start with its ID, and its gRPC package is frazer.<service>.v1.

Send a token in the Authorization header:

Terminal window
curl https://api.frazer.ai/resourcemanager/v1/projects/acme-staging \
-H "Authorization: Bearer $FRAZER_TOKEN"
Credential Use it for Lifetime
Personal access token (frz_pat_…) Scripts and tools you run Expiry required: up to 365 days, 90 by default; an organization can cap it
Workload identity federation CI jobs and workloads, with no stored key 15-minute tokens
Service account key Systems that cannot federate; off by default Up to 90 days when the organization allows keys
CLI sign-in (frazer login) Your terminal 15-minute access tokens, refreshed for up to 90 days
OAuth 2.1 (MCP clients and apps) Agents and connected apps 15-minute access tokens, refreshed for up to 30 days

Create one under Account › Personal access tokens and CLI tokens in the console. Creating a token needs a passkey or second factor from the last 10 minutes, and a token can never create another token. Frazer shows the token once and stores only its hash.

  • A token is bound to one organization. A call on another organization’s resource fails with ORG_RESTRICTED.
  • A token carries scopes, and it never allows more than you hold yourself.
  • allowed_networks limits a token to up to 20 IPv4 or IPv6 prefixes; a call from anywhere else is refused before any permission check.
  • The token page shows when and from which address each token was last used. Frazer emails you 7 days and 1 day before a token expires.
Scope Allows
<service>:read The service’s read and list permissions, for example kubernetes:read
<service>:write Every permission of the service except IAM changes and data access
cloud-platform Every permission you hold

A permission outside a token’s scopes is denied with deniedBy: token_scope.

Every access token names one audience: https://api.frazer.ai or https://mcp.frazer.ai. A token minted for one is refused by the other. Access tokens are signed JWTs.

Tokens the CLI holds are bound to the device’s key (DPoP): they travel as Authorization: DPoP with a fresh proof on each request, and the same token sent as Bearer is refused. For scripts, use a personal access token or workload identity federation.

A GitHub Actions or GitLab job, or a workload in a Frazer cluster, exchanges its own signed identity token for a 15-minute Frazer token. A condition on the token’s claims decides which jobs may, for example repository == "acme/platform-envs" && ref == "refs/heads/main". Nothing secret is stored in CI. See Identity and access.

All protocols are served on api.frazer.ai, on one port, by the same handlers.

Terminal window
curl https://api.frazer.ai/resourcemanager/v1/projects/acme-staging \
-H "Authorization: Bearer $FRAZER_TOKEN"
  • REST paths are https://api.frazer.ai/<service>/v1/<resource name>. Custom methods end in :verb, for example POST https://api.frazer.ai/iam/v1/organizations/acme/decisions:troubleshootAccess.
  • gRPC and Connect use the service names of the contracts, such as frazer.resourcemanager.v1.ProjectService or frazer.approvals.v1.ApprovalService. gRPC-Web is served too.
  • Streams such as WatchOperation exist on gRPC and Connect only. REST clients poll instead.
  • OpenAPI 3.1 descriptions of the REST surface and the Go (go.frazer.ai/sdk) and TypeScript (@frazer-ai on npm) SDKs are generated from the same contracts.

Every resource has a name that is also its path:

organizations/acme
projects/acme-production
projects/acme-production/locations/us-virginia1/clusters/acme-production-1
projects/acme-production/managedZones/apps
organizations/acme/decisions/<decision>
  • Projects first. Project IDs are unique across Frazer, so a project’s resources are named from the project. The organization is the project’s parent, not part of the name, so moving a project to another organization renames nothing. Only a small set of collections, such as decisions and verified domains, live under organizations/<organization>.
  • Locations. Regional resources carry locations/<location>, a region such as us-virginia1 (Virginia, USA). global is a location too.
  • IDs you choose are lowercase letters, digits and hyphens, start with a letter and are at most 63 characters. Project IDs are 6 to 30 characters and never reused; organization IDs are 4 to 24. IDs starting with frazer, goog or system are reserved. Every resource also has a uid that is never reused.
  • Full resource names add the service: //kubernetes.frazer.ai/projects/acme-production/locations/us-virginia1/clusters/acme-production-1. Events and Terraform imports use them, and https://console.frazer.ai/r/<full resource name> opens any resource in the console.

Resources share standard fields:

Field Meaning
name, uid The resource name and the system ID
display_name, labels, annotations Your descriptions. Labels are metadata only and never used in policy; tags are
create_time, update_time, delete_time, purge_time When it was created, changed, deleted and will be purged
etag The version, for safe updates
state, reconciling Its state, and whether Frazer is still converging it
generation, observed_generation The version of the spec, and the one Frazer has applied

Labels: up to 64 per resource; keys and values up to 63 characters of a-z, 0-9, _ and -.

Method REST Notes
Get GET on the name
List GET on the collection Pagination and filtering
Create POST on the collection You choose the ID
Update PATCH on the name Only the fields in update_mask change
Delete DELETE on the name A soft delete; see Soft delete
Undelete POST on :undelete Before purge_time
Custom POST on :verb Such as :upgrade or :troubleshootAccess

Every mutation takes validate_only and request_id; every mutable resource has an etag.

A mutation that can take more than a second returns an operation:

  • GetOperation and ListOperations read operations; done operations stay readable for at least 30 days.
  • WatchOperation streams progress on gRPC and Connect, and a watch that breaks resumes where it stopped. Over REST, poll GetOperation.
  • CancelOperation cancels where a method supports it, and answers CANCEL_NOT_SUPPORTED where it does not.
  • An operation’s name is projects/<project>/locations/<location>/operations/<operation>, or organizations/<organization>/operations/<operation> for an organization-level resource.

Every mutation accepts validate_only. Frazer runs every check of the real call (permissions, organization policy, quota and cost) and changes nothing; no audit record is written. The answer is the plan:

  • A long-running method returns a done operation with no name: its metadata.plan is the plan, and its response is the resource as it would be.
  • A method that answers at once returns the resource as it would be.
Plan field Meaning
changes Each resource created, changed or deleted: the action, the resource and its type
quota_impacts Per quota counter: the metric, scope, location, limit, usage and the amount requested
policy_violations Organization policy the change would break
estimated_cost The cost per month, as a currency amount
warnings For example, a counter at 80 % or more of its limit after the change

A plan over a quota fails with the same QUOTA_EXCEEDED error the call itself would return, with the numbers and a link to request more. frazer … --dry-run, the console’s Plan step, MCP write tools and terraform plan all use validate_only.

When a rule holds a call for a person’s approval, the call changes nothing and answers FAILED_PRECONDITION with reason APPROVAL_REQUIRED in domain approvals.frazer.ai:

  • metadata approval (its name), planHash, expireTime, rule and state;
  • a Help link to the approval’s page, https://console.frazer.ai/approvals/<approval name>;
  • the plan, and the X-Frazer-Approval response header.

Retrying with the same request_id returns the same approval. Once approved, Frazer applies the call itself, exactly once, as the caller, and with the plan it showed: if the target changed in the meantime, the approval fails with PLAN_CHANGED. Approvals are resources of the approvals service (GetApproval, ListApprovals; :approve, :reject, :cancel), and approving needs a person with a passkey or second factor from the last 10 minutes. See Identity and access.

List methods take page_size and page_token and return next_page_token. Pass the token back unchanged to get the next page; it is opaque and signed. An empty next_page_token means the last page. Pages are read by key, never by offset.

Where a method allows it, projects/- as the parent lists across every project you can read. Results from places Frazer could not reach are named in unreachable rather than silently left out.

List methods take a filter in the AIP-160 syntax and an order_by on the fields the method allows:

can_decide = true AND state = PENDING

A list returns what you may see: everything when you may list the collection, otherwise the resources you were granted directly.

Every mutation takes a request_id, a UUID you generate. A retry with the same ID and the same request returns the first answer for 7 days, so a create that timed out never runs twice. The same ID with a different request fails with REQUEST_ID_REUSED. The CLI sets a request ID on every invocation, and the Terraform provider keeps one per create in its state.

Every mutable resource has an etag. Send it with an update or delete; if the resource changed since you read it, the call fails with ABORTED and reason ETAG_MISMATCH. Read the resource again, reapply your change and retry. Some methods require an etag, such as setting a whole IAM policy.

Delete is soft: the resource gets a delete_time and a purge_time, and Undelete restores it until then. A deleted project, for example, stops billing and suspends its resources at once, and can be restored for 30 days; after that it is purged and its ID stays reserved. A lien on a project blocks its deletion with LIEN_PRESENT.

Frazer admits every request before it does any work:

  • Rate limits per principal and per organization, weighted by each method’s cost; large pages cost more.
  • Fair queues per organization, then per project, so one organization’s burst does not starve another.
  • Priorities: interactive calls from the console and CLI first, then automation (MCP, Terraform, SDKs and service accounts), then batch work such as exports and large lists.

A refused request gets RESOURCE_EXHAUSTED (HTTP 429) with reason RATE_LIMITED or CONCURRENCY_LIMITED, or OVERLOADED when Frazer is shedding load, each with a RetryInfo delay. Wait at least that long before you retry, and retry a mutation only with the same request_id.

Version Promise
v1 Stable: no breaking changes. A new major version comes with at least 12 months’ notice
v1beta1 At least 90 days’ notice before a breaking change
v1alpha1 May change; an organization opts in per feature

A deprecated method or field answers with a Deprecation response header, the CLI warns, MCP tools are marked, and a deprecation.announced event goes out. Error reasons and metadata keys are added but never removed or reused; treat a value you do not know as the generic case.

Errors follow one model on every protocol: a status code, an ErrorInfo with a reason, the domain of the service that refused (<service>.frazer.ai) and metadata, plus details such as field violations, QuotaFailure, RetryInfo and Help links.

403 over REST
{
"error": {
"code": 403,
"status": "PERMISSION_DENIED",
"message": "The caller does not have the permission this method needs on the resource (or it might not exist).",
"details": [
{
"@type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "PERMISSION_DENIED",
"domain": "iam.frazer.ai",
"metadata": {
"permission": "kubernetes.clusters.get",
"resource": "projects/acme-production/locations/us-virginia1/clusters/acme-production-1",
"deniedBy": "allow",
"rolesGranting": "roles/kubernetes.viewer",
"decisionId": "0192f3a1-7c2e-7b4d-9e10-3f6a2b8c5d47"
}
}
]
}
}

Errors explains every field, how to replay a denial by its decision ID, and every reason with its fix.

Header When
X-Frazer-Authz-Decision Every response: the ID of the permission decision, allowed or not
X-Frazer-Approval The call is held: the approval’s name
X-Frazer-Iam-Revision After an IAM change: the organization’s IAM revision
X-Frazer-Access-Change After an IAM change: EQUIVALENT, MORE_PERMISSIVE, LESS_PERMISSIVE or INCOMPARABLE
Deprecation The method or a field you used is deprecated