Custom Actions
Resource-oriented design AEP-121 uses custom actions to provide a means to express actions that are difficult to model using only the standard actions. Custom actions are important because they provide a means for an API’s vocabulary to adhere to user intent.
Guidance
Section titled “Guidance”- Custom actions must use POST or GET.
- Custom actions must not use
PUT,PATCHorDELETE. - If the operation results in any change in server state, it must use POST.
- Only actions which are idempotent, safe, and cacheable may use GET.
- Custom actions must not use
- The HTTP URI must use a
:character followed by the custom verb (e.g.:cancel).- If word separation is required,
camelCasemust be used. - The custom verb must be placed after the resource identifier (e.g.,
/orders/123:cancel).
- If word separation is required,
- The custom verb must be an action verb describing what it does.
- It must not contain nouns or noun phrases.
- It must not contain prepositions (“for”, “with”, etc.).
- It should be concise and clearly convey the operation’s intent.
- Custom actions must operate on a specific resource or collection.
- They must not be standalone endpoints unrelated to resources.
- Custom actions should avoid redundancy with the resource name (e.g.,
:cancelnot:cancelOrderwhen the resource is already an order)
APIs must clearly document each custom action, including:
- The purpose and semantics of the operation.
- Whether the operation is idempotent.
- Expected request body structure (for
POSTmethods). - Expected response structure and status codes.
- Any preconditions or state requirements for the operation to succeed.
- Side effects of the operation.
When to use custom actions
Section titled “When to use custom actions”Custom actions should only be used for functionality that cannot be expressed via standard actions and reified resources; prefer those over custom actions due to their consistent semantics.
Use custom actions when:
- The operation represents a simple action or state transition with no
additional data or history requirements (e.g., “cancel order” when you don’t
need to track who canceled it or why).
- Meaning, the operation is instantaneous, has no state, and leaves no trace worth tracking.
- Using standard actions would require awkward or unintuitive resource modeling that obscures the operation’s intent.
- The action is better expressed as a verb acting on a resource rather than a state change on the resource itself.
Do NOT use custom actions when:
- Standard actions can naturally express the operation.
- The operation has state that should be tracked, monitored, or queried; use
reification to model it as a resource instead (e.g.,
/imports,/deployments,/calculations). - You’re simply trying to avoid thinking about resource modeling; take time to consider if a resource-oriented approach would be clearer.
Idempotency
Section titled “Idempotency”Custom actions using GET must be idempotent by definition of the HTTP specification.
Custom actions using POST are not inherently idempotent. If a custom action is idempotent, this must be clearly documented. Custom actions that require idempotency (such as payment operations or order submissions) should support an Idempotency-Key.
Bulk Actions
Section titled “Bulk Actions”Custom actions may be used for bulk operations. This is especially useful
when it would otherwise be ambiguous or conflict with single-resource
operations. For example, POST /books is used for creating a single book, to
avoid conflicting URIs, bulk creation may use a custom action:
POST /books:batchCreateContent-Type: application/json
{ "books": [ {"title": "Book 1", "author": "Author A"}, {"title": "Book 2", "author": "Author B"} ]}Similarly, bulk updates may use a custom action when updating multiple resources at once.
However, API authors should first consider whether reifying the bulk operation as a resource would be more appropriate. For example:
POST /books/importsPOST /books/batchesPOST /books/uploads
Stateless methods
Section titled “Stateless methods”Some custom actions are not attached to resources at all. These actions are generally stateless: they accept a request and return a response and have no permanent effect on data within the API.
paths: /projects/{projectId}:translate: post: operationId: translate description: Translates the provided text from one language to another. requestBody: content: application/json: schema: description: Request structure to translate text. properties: contents: type: array items: type: string description: The contents of the input, as a string. sourceLanguageCode: type: string description: | The BCP-47 language code of the input text (e.g. "en-US"). If the source language is not specified, the service will attempt to infer it. targetLanguageCode: type: string description: | The BCP-47 language code of the output text (e.g. "en-US"). responses: '200': description: OK content: application/json: schema: description: | Response structure for the translate operation. properties: translatedText: type: string description: | Text translated into the target language. detectedLanguageCode: type: string description: | The BCP-47 language code of source text in the initial request, if it was detected automatically.Usage in declarative clients
Section titled “Usage in declarative clients”APIs muat not employ custom actions for functionality that is intended to be used in a declarative client. Declarative clients use only standard actions to apply desired state, and integration of custom actions is manual and results in client-side complexity around state management to determine when the custom method should be invoked.
Changelog
Section titled “Changelog”- 2026-03-06: Change word separation from kebab to camel case. Import bulk operations section from AEP-130.
- 2026-02-20: Change verbiage from
methodtoaction. Remove filtering. Add Declarative clients and stateless methods. - 2024-12-10: Initial creation, adapted from Google AIP-136 and aep.dev AEP-136.