Resource names

How resources are addressed, and how to build a name you have not seen before.

Every resource on SPEKTRA Edge has one canonical name, and that name is the only identifier you need. It is the path in a REST URL, the name field in a gRPC request, and the argument you pass to cuttle. Learn to construct one and most of the API stops needing to be looked up.

The shape of a name

A name is an alternating sequence of collection and identifier segments, reading left to right from the outermost scope inward:

projects/acme-retail/regions/us-west2/devices/till-004

which is to say: the device till-004, in region us-west2, of project acme-retail. Collections are plural and lowerCamelCase; identifiers are yours.

Because the structure is regular, you can read the parent of any resource by dropping the last two segments — the parent of that device is projects/acme-retail/regions/us-west2.

Name scopes

How many segments a name has depends on the resource, and this is the single most common cause of a confusing NOT_FOUND. There are four patterns.

Pattern Example Notes
Global projects/acme-retail Projects, organizations, and read-only catalogs such as device types and OS versions.
Project projects/acme-retail/distributions/pos-app Project-wide, not tied to a region.
Project and region projects/acme-retail/regions/us-west2/devices/till-004 Most operational resources.
Nested under another resource projects/acme-retail/regions/us-west2/serviceAccounts/automation/serviceAccountKeys/ci The parent is itself a full name.

Some resources nest more deeply than you might expect. An alerting condition lives under a policy, and an alert lives under a condition:

projects/{project}/regions/{region}/alertingPolicies/{policy}/alertingConditions/{condition}/alerts/{alert}

A few resources accept more than one kind of parent, though any single instance has exactly one. A role binding can be scoped to a project, an organization, or a service:

projects/{project}/roleBindings/{roleBinding}
organizations/{organization}/roleBindings/{roleBinding}
services/{service}/roleBindings/{roleBinding}

When in doubt, the per-resource entry under Service APIs lists the name patterns it accepts.

Names are permanent

A name identifies a resource for its whole life and cannot be changed. Anything you might want to edit later — a human-readable label, a location, ownership — belongs in a field such as displayName, not in the name.

On create you may either supply the final identifier or omit it and let the platform generate one. Supply it when the name should be predictable from something you already know, such as a store number; omit it when the resource is one of many and you will find it by filtering on labels instead.

Identifier segments are constrained. For most resources the last segment must match:

[a-z][a-z0-9\-]{0,28}[a-z0-9]

Lower case, starting with a letter, ending alphanumeric, at most 30 characters. Some resources differ — service accounts also allow underscores and permit longer names — so check the resource’s reference entry before generating identifiers programmatically.

Wildcards in collection requests

When reading a collection you can substitute - for an identifier to mean “across all of them”. This is how you query a whole project without knowing its regions:

# Devices in one region
cuttle devices list devices --parent projects/$PROJECT/regions/us-west2

# Devices in every region of the project
cuttle devices list devices --parent projects/$PROJECT/regions/-

It works at any level, which matters most for the deeply nested resources. Listing alerts across every policy and condition in a project would otherwise require enumerating both:

cuttle monitoring list alerts \
  --parent "projects/$PROJECT/regions/-/alertingPolicies/-/alertingConditions/-"

Wildcards apply to reads. Creating, updating, or deleting requires a fully specified name.

From a name to a REST URL

REST paths are the resource name with a version prefix, so once you can build a name you can build a URL. Every service answers on its own host, shaped {service}.apis.edgelq.com.

The version prefix belongs to the service, not to the platform, so do not assume v1 everywhere. Most services are on v1, but Monitoring is on v4 (https://monitoring.apis.edgelq.com/v4/...). Check the service’s page under API reference before building URLs by hand.

Operation Method and path
Get GET /v1/projects/{project}/regions/{region}/devices/{device}
List GET /v1/projects/{project}/regions/{region}/devices
Create POST /v1/projects/{project}/regions/{region}/devices
Update PUT /v1/projects/{project}/regions/{region}/devices/{device}
Delete DELETE /v1/projects/{project}/regions/{region}/devices/{device}

Anything that is not plain CRUD is a verb appended after a colon, which is why these URLs look unusual at first:

GET  /v1/projects/{project}/regions/{region}/devices:search
GET  /v1/devices:batchGet
POST /v1/projects/{project}/regions/{region}/devices:watch
POST /v1/projects/{project}/regions/{region}/devices/{device}:getDedicatedEndpoints

Two consequences follow. batchGet is not scoped to a parent — it takes the names you want as parameters, so it can span regions in one call. And some read-only operations are POST, because their request does not fit in a URL; projects:listMy is the one you are most likely to meet.

Regions

A project belongs to a region, and most operational resources are created within one. The region in a name is not a routing hint you may omit — it is part of the resource’s identity, and the same identifier in two regions is two different resources.

Practically:

  • Use the region your project reports. It is shown in the dashboard and in the project resource itself.
  • To search across regions, use the - wildcard instead of looping.
  • Secrets do not replicate across regions, so a multi-region fleet needs the secret created in each region its devices occupy. See Secrets.

Next steps