AIP-122
Resource IDs and URI Paths
Most APIs expose resources (their primary nouns) which users are able to create, retrieve, and manipulate. Each resource has a unique identifier (ID) and a URI path that users use to reference that resource.
Guidance
All resource IDs defined by an API must be unique within their collection.
Resource URI paths follow a flat model with collection identifier and resource ID:
v1/invoices/8474b73c-b4ae-4b66-9f0f-bbdbcd9c108b
v1/claims/ca2947ab-4f3c-46de-ad72-99c53b750e3c
- URI paths should follow the pattern of collection identifiers
(example:
invoices,claims,subscriptions) followed by server-defined resource IDs (example:8474b73c-b4ae-4b66-9f0f-bbdbcd9c108b). - URI paths must use the
/character to separate the collection identifier from the resource ID. - Resources must expose an
idfield that contains its resource ID.
Collection identifiers
The first part of a resource URI path is the collection identifier, which must
be the plural form of the resource type. For example, an Invoice resource type would
use invoices as its collection identifier.
- Collection identifiers must be concise American English terms.
- Collection identifiers must be in
snake_case. - Collection identifiers must contain only lower cased ASCII letters, numbers,
and underscores (
/[a-z][a-z0-9_]*/). - Collection identifiers must be plural.
- In situations where there is no plural word ("info"), or where the singular and plural terms are the same ("moose"), the non-pluralized (singular) form is correct.
Resource IDs
The resource ID is the identifier that uniquely identifies a resource within its collection. In
the URI path invoices/8474b73c-b4ae-4b66-9f0f-bbdbcd9c108b,
8474b73c-b4ae-4b66-9f0f-bbdbcd9c108b is the resource ID for the invoice.
- Resource IDs must be globally unique within the collection.
- Resource IDs must be one of the following:
- Server-generated UUIDs (example:
8474b73c-b4ae-4b66-9f0f-bbdbcd9c108b) - User-defined identifiers (example:
acme-corp)
- Server-generated UUIDs (example:
- User-defined resource IDs must only contain lower-cased ASCII letters, numbers or
hyphens (
/^[a-z][a-z0-9-]*[a-z0-9]$/) - Resources created through public-facing APIs must use server-generated UUIDs.
- Resources created through internal APIs may use user-defined identifiers when appropriate.
Fields representing resource IDs
When defining a resource, the resource ID field must be of type string and must be called id.
The field must be annotated with the [(google.api.field_behavior) = IDENTIFIER] tag.
// A representation of an invoice.
message Invoice {
// The resource ID of the invoice.
string id = 1 [(google.api.field_behavior) = IDENTIFIER];
// Other fields...
}
Note: Fields must not be called id except for this purpose. For
other use cases, either use a different term or prepend an adjective (for
example: display_name).
Fields representing external identifiers
Clients often have their own identifiers for resources:
- Client-provided identifiers must be stored in a field named
external_id. - Resources representing third-party integrations may use
integration_idfor identifiers from external systems. - Resources integrating with multiple third-party systems may include an
integration_typeenum field to identify the source system. - Resources must not reference other resources by their
external_id. - API methods must identify resources exclusively by their
idfield and must not accept or useexternal_idvalues as resource identifiers.
Fields representing another resource
When a field represents another resource, the field should be of type
string and accept the resource ID of the other resource.
- Field names should be formatted as resource name followed by an
_idsuffix (e.g.,invoice_id,claim_id).
The field should not be of type message using the message that implements the
resource, except when both of the following conditions are met:
- The API has tight lifecycle relationships.
- The API has a permission model that enables inherited access to embedded resources.
When expanding a resource message, the following requirements apply:
- The expanded resource message field must be annotated with
[(google.api.field_behavior) = OUTPUT_ONLY]. - The primary resource must include the expanded resource's ID as a separate
field (e.g.,
invoice_id). - The expanded resource message must always be populated in responses, if present.
- Clear documentation must be provided explaining that the expanded resource does not belong to the primary resource.
Example of a resource reference:
// A representation of an invoice.
message Invoice {
// ID of the invoice.
string id = 1 [(google.api.field_behavior) = IDENTIFIER];
// The subscription this invoice is for.
string subscription_id = 2;
// Other fields...
}
Rationale
Resource ID Patterns
While server-generated UUIDs are generally preferred for most resources, user-defined resource IDs are allowed in specific scenarios where human readability provides clear benefits:
- Resources where natural uniqueness exists (e.g., merchant names, brand identifiers).
- Resources that benefit from deterministic, human-readable IDs for easier reference.
- Resources managed through internal APIs where ID conflicts can be controlled.
By restricting user-defined IDs to internal-facing APIs, we maintain control over ID generation while providing flexibility where appropriate. The strict pattern requirements ensure consistency and prevent potential issues with ID parsing or display.
Discourage resource expansion
Using a resource message directly as the type of a field within another resource is problematic for a number of reasons, which are as follows:
- Complicates the resource lifecycle: If the dependency resource is deleted, what happens to the embedded reference in the dependent resource? Data retention and clean up operations will be significantly complicated.
- Bypasses permissions: If every resource has its own set of permissions, a user with read permission on the dependent resource that doesn't have the same permission on the dependency resource suddenly cannot see the full resource.
- Tightly couples resources in all aspects: Changing the requirements in the schema, permissions, or otherwise for either resource impacts the other, significantly increasing complexity of roll outs.
Referencing by id, as is recommended, eliminates all of this complexity by preventing resource data duplication, and forcing the owning service to be involved in the resolution of the reference (via Standard Methods), guaranteeing isolation of logical concerns per-resource.
While referencing by ID is strongly preferred, there are exceptional cases where expanding a tightly coupled resource can be appropriate:
- When the dependent resource cannot meaningfully exist without the primary resource.
- When clients would need to make additional API calls to retrieve the dependent resource in nearly all use cases.
In these situations, nesting can improve API usability by reducing the number of required API calls. However, it remains important to document the relationship between resources clearly, explaining that the expanded resource is not owned by the containing resource and clarifying the lifecycle implications.
Disallow nested resource paths
This API design avoids representing parent-child relationships in URI paths for several reasons:
- Parent-child URI paths create artificial hierarchies that couple resources together, making API evolution difficult.
- Nested paths imply ownership that may not reflect actual business relationships.
- Resources that belong to multiple parents would need to be duplicated across different paths.
Instead, relationships between resources are expressed through reference fields
(e.g., customer_id in a subscription resource).
History
User-Specified Resource IDs
Public-facing APIs previously allowed user-specified resource IDs in the id
field during resource creation. In these cases, the ID was typically the client's
ID. This approach created several challenges:
- User-specified IDs were not guaranteed to be unique across different clients.
- Without expanded resources, we had to request both the client-specified ID and a store ID for proper identification.
- References to other resources became complicated and error-prone.
We addressed these issues by switching to server-generated UUIDs for resource IDs and
moving client-specified identifiers to an external_id field. This change simplified
resource references and improved API consistency.
Some legacy resources (such as orders) still maintain the original pattern due
to backward compatibility requirements.
Divergence from Google AIP
This AIP diverges from Google's AIP-122 in several key ways:
- We use a flat resource model instead of Google's hierarchical resource names.
- We use
id(containing only the resource ID) as the primary identifier field instead ofname(containing the full path). - We prohibit nested URI paths that Google uses to encode parent-child relationships.
- We reference resources using IDs with an
_idsuffix rather than full resource names. - We use server-generated UUIDs for public-facing APIs while allowing user-defined identifiers for internal APIs under specific constraints.
- We added guidance on handling client-provided identifiers through
external_idandintegration_idfields. - We discourage but do not strictly prohibit resource message expansion in specific cases where it improves API usability, whereas Google's approach is more restrictive.
- We removed resource patterns, parent field requirements, and other hierarchical concepts.
These changes simplify API design and allow resources to evolve independently, inspired by Stripe's API design.
Changelog
- 2025-05-20: Added history section on user-specified resource IDs.
- 2025-05-20: Initial version forked from Google's AIP-122 with significant changes to resource identification model, including flat resource paths, server-generated UUIDs, and simplified resource references.