31. Pattern: Intent as a Resource
The Intent as a Resource pattern addresses scenarios where a business capability cannot be completed in a single, synchronous HTTP request. By modeling the workflow itself as a RESTful resource, clients can initiate a long-running process, monitor its state, and asynchronously provide additional information required to push the transaction to completion.
This approach perfectly complements Intent-Based Design. When an intent requires multi-step orchestration, human intervention, or asynchronous validation, the API captures the intent in a state machine rather than forcing the client to maintain the transaction state.
31.1. Overview
While a simple product return might be resolvable in one synchronous POST /product-returns request, real-world enterprise processes are rarely that clean. An electronics return might require the customer to upload photos of the damaged item before an RMA (Return Merchandise Authorization) is issued. A loan application might require a credit freeze to be lifted before processing can continue.
If an API attempts to handle this synchronously, the connection will time out. If the API relies on the client to remember where they are in the process, the integration becomes fragile and prone to data loss if the client disconnects.
The Intent as a Resource pattern solves this by instantiating the process on the server:
- Initiation: The client submits the initial intent (
POST /returns). The server creates a workflow resource and returns a201 Createdwith a status ofpending_information. - Continuation: The client provides the missing pieces via a mutation to the workflow resource (e.g.,
PATCH /returns/{returnId}or a functionalPOST /returns/{returnId}/photos). - Completion: Once all constraints are satisfied, the server transitions the workflow to a terminal state (
approvedorrejected) and executes the underlying business logic.
31.2. When to Use the Intent as a Resource Pattern
Use the INtent as a Resource pattern when:
- The business process is asynchronous or long-running.
-
e.g., Video transcoding, background data migrations, or document verification.
- The process requires multi-step data collection (Wizards).
-
Instead of saving partial state in a frontend browser cache, the client saves progress to a server-side workflow resource that can be resumed from any device.
- A transaction is blocked pending external input.
-
e.g., A return requires a customer service representative to review a photo of a damaged item before the workflow can proceed to the
refundedstate. - You need strict audit trails for complex interactions.
- Because the workflow is a first-class resource, you can easily log every state transition, timestamp, and user action associated with the process.
31.3. When NOT to Use the Intent as a Resource Pattern
Avoid the Intent as a Resource pattern when:
- The operation can be resolved quickly and synchronously.
-
If a process takes less than a few seconds and requires no mid-flight intervention, a standard functional
POST(e.g.,POST /reservations/{id}/change-flight) is vastly simpler for clients to consume. - The data is purely local/ephemeral.
- Do not use server-side workflow resources to track UI states (like expanding/collapsing menus or sorting preferences) that have no bearing on backend business logic.
31.4. What the Pattern Looks Like
Below is an HTTP interaction flow demonstrating a product return workflow that is paused pending photographic evidence of damage, and subsequently resumed.
1. Initiating the Workflow
The client declares the intent to return a damaged item.
Request
POST /returns HTTP/1.1
Host: api.example.com
Content-Type: application/json
{
"orderId": "ord_8821",
"itemId": "prd_104",
"reason": "damaged_in_transit"
}
Response
The server determines that “damaged” returns require photo verification. It creates the workflow resource in a pending_photos state and provides hypermedia links indicating the next required action.
HTTP/1.1 201 Created
Location: https://api.example.com/returns/ret_992
Content-Type: application/json
{
"id": "ret_992",
"orderId": "ord_8821",
"status": "pending_photos",
"message": "Please upload photos of the damaged item to proceed.",
"links": {
"self": "/returns/ret_992",
"upload_photos": "/returns/ret_992/photos"
}
}
2. Updating the Workflow (Providing Additional Info)
The client follows the provided link and uses a functional POST (or a PATCH if updating text fields) to supply the missing requirements.
Request
POST /returns/ret_992/photos HTTP/1.1
Host: api.example.com
Content-Type: application/json
{
"photoUrls": [
"https://storage.example.com/uploads/dmg_1.jpg",
"https://storage.example.com/uploads/dmg_2.jpg"
]
}
Response
The server accepts the information, evaluates the rules, and transitions the workflow resource to an approved state, providing the final resolution details.
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": "ret_992",
"status": "approved",
"refundMethod": "original_payment",
"refundEstimate": "$89.00",
"labelUrl": "https://api.example.com/labels/1Z999AA1.pdf",
"carrier": "UPS"
}
31.5. Anti-Patterns to Avoid
- Blocking HTTP connections for long-running steps:
-
Never hold a
POSTrequest open for minutes waiting for a background worker to finish. Return a202 Acceptedor201 Createdimmediately and instruct the client to poll the workflow resource (or subscribe to a webhook). - Losing the workflow ID:
-
If the client initiates a workflow but fails to store the returned
idorLocationheader, the workflow becomes an orphaned, dangling transaction. APIs should expose endpoints likeGET /returns?status=pendingso clients can recover abandoned workflows. - Allowing infinite state transitions:
- A workflow resource must have strict state machine validations. A client should not be able to
PATCHanapprovedreturn back into apending_photosstate. Reject invalid transitions with a409 Conflict.
31.6. OpenAPI Example
An OpenAPI 3.0.3 specification illustrating the endpoints for managing the lifecycle of a Return Workflow resource.
openapi: 3.0.3
info:
title: Product Returns API - Workflow Resource
version: 1.0.0
servers:
- url: https://api.example.com
paths:
/returns:
post:
summary: Initiate a return workflow
description: Starts a new return process. May complete immediately or enter a pending state requiring further action.
tags: [Returns]
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ReturnInitiation'
responses:
'201':
description: Workflow created
content:
application/json:
schema:
$ref: '#/components/schemas/ReturnWorkflow'
/returns/{returnId}/photos:
post:
summary: Provide required photos
description: A functional endpoint to submit evidence for a return currently stuck in the `pending_photos` state.
tags: [Returns]
parameters:
- in: path
name: returnId
required: true
schema:
type: string
example: "ret_992"
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
photoUrls:
type: array
items:
type: string
responses:
'200':
description: Photos accepted and workflow updated
content:
application/json:
schema:
$ref: '#/components/schemas/ReturnWorkflow'
'409':
description: Conflict - Workflow is not in a state accepting photos
components:
schemas:
ReturnInitiation:
type: object
required: [orderId, itemId, reason]
properties:
orderId:
type: string
itemId:
type: string
reason:
type: string
enum: [wrong_item, damaged_in_transit, buyer_remorse]
ReturnWorkflow:
type: object
properties:
id:
type: string
status:
type: string
enum: [pending_photos, processing, approved, rejected]
message:
type: string
refundEstimate:
type: string
labelUrl:
type: string
links:
type: object
properties:
self:
type: string
upload_photos:
type: string
31.7. Visualizing the Workflow Resource (Mermaid Diagram)
sequenceDiagram
autonumber
actor Client
participant API as API Server
participant DB as Workflow Database
Note over Client,DB: Phase 1: Initiate Workflow
Client->>API: POST /returns (reason: damaged)
API->>API: Evaluate rules (Damage requires evidence)
API->>DB: Create workflow (status: pending_photos)
DB-->>API: Return ID (ret_992)
API-->>Client: 201 Created (status: pending_photos)
Note over Client,DB: Phase 2: Client provides missing info
Client->>API: POST /returns/ret_992/photos
API->>DB: Fetch workflow ret_992
API->>API: Verify photos & approve return
API->>DB: Update workflow (status: approved)
API-->>Client: 200 OK (status: approved, includes Shipping Label)