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 a 201 Created with a status of pending_information.
  • Continuation: The client provides the missing pieces via a mutation to the workflow resource (e.g., PATCH /returns/{returnId} or a functional POST /returns/{returnId}/photos).
  • Completion: Once all constraints are satisfied, the server transitions the workflow to a terminal state (approved or rejected) 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 refunded state.

  • 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 POST request open for minutes waiting for a background worker to finish. Return a 202 Accepted or 201 Created immediately 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 id or Location header, the workflow becomes an orphaned, dangling transaction. APIs should expose endpoints like GET /returns?status=pending so 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 PATCH an approved return back into a pending_photos state. Reject invalid transitions with a 409 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)