# Pattern: asynchronous actions an agent can finish

> A concrete request-to-result contract with explicit states, retry behavior, authorization, and verifiable completion.

Canonical: https://byagents.dev/agents/action-contract-pattern
Published: 2026-09-13
Reviewed: 2026-09-13
Publisher: ByAgents
Type: Pattern
Level: Technical
Outcome: Review an asynchronous API for missing steps and define a complete first-result path.

This is a fictional API design pattern, not an endpoint available from ByAgents and not a normative MCP or commerce schema. Adapt it to the actual service, security model, and charging policy. The central requirement is a documented path from request to terminal outcome.

## Define the lifecycle before the endpoints

```text
request validated -> queued -> running -> succeeded
                                  -> failed
                                  -> cancelled
```

State which transitions are possible and who can trigger them. If cancellation is unsupported, omit it. Define whether cancellation prevents a charge or only stops future work. A job reaching a terminal state should remain retrievable for a documented retention period.

## Specify the operations

| Operation | Required contract details |
| --- | --- |
| POST /v1/extractions | Auth scope, validated input reference, idempotency key, charging boundary |
| GET /v1/extractions/{id} | Same account authorization, state enum, polling behavior, terminal result |
| Result retrieval | Inline output or a documented authorized URL, type, expiry, and size limits |

Prefer an authorized upload or input identifier over unrestricted remote-URL ingestion. If accepting remote URLs, implement fetch restrictions and validation; a public action endpoint should not become a proxy to private networks.

## Example accepted response

```http
HTTP/1.1 202 Accepted
Content-Type: application/json
Location: /v1/extractions/ex_example_42
Cache-Control: no-store
```

```json
{
  "id": "ex_example_42",
  "state": "queued",
  "status_url": "/v1/extractions/ex_example_42",
  "poll_after_seconds": 3,
  "result": null,
  "error": null
}
```

The polling field is an application-defined example. Define a maximum polling budget and what the caller should do if it expires. A 202 response acknowledges acceptance; it does not prove the extraction is complete. Document how relative URLs resolve against the service origin and preserve authorization on subsequent requests.

## Example successful terminal response

```json
{
  "id": "ex_example_42",
  "state": "succeeded",
  "result": {
    "currency": "USD",
    "line_items": [
      { "description": "Example part", "quantity": 2, "unit_amount_minor": 1250 }
    ]
  },
  "error": null
}
```

This example defines unit_amount_minor as an integer in the currency's minor unit. Do not assume another API uses that convention. The actual schema should constrain types, state-specific fields, numeric ranges, and size limits. Verify the result against a fixture rather than accepting any non-empty JSON as success.

## Define retry semantics precisely

An idempotency key should identify one intended operation within a defined account scope and retention window. Specify how repeated equivalent requests retrieve the same operation and how conflicting reuse is rejected. The server must enforce this behavior atomically; a documentation sentence alone does not prevent duplicates.

A timeout may occur after acceptance. Let the caller reconcile the original request before creating another one. Distinguish retryable conditions from validation, permission, and exhausted-budget failures. Bound retries and respect the service's documented backoff. Do not retry a paid action merely because the agent did not see the first response.

## Return actionable errors without leaking internals

RFC 9457 defines a standard problem-details representation for HTTP APIs. It can carry a stable problem type and readable explanation without inventing a generic 200 response for failures. [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457.html).

```json
{
  "type": "https://example.com/problems/unsupported-input",
  "title": "Unsupported input type",
  "status": 422,
  "detail": "This example operation accepts PDF inputs only."
}
```

Return the matching HTTP status and application/problem+json when using that format. Define documented error types and permitted recovery. Keep tokens, stack traces, private document contents, and internal network details out of public errors.

## Acceptance checks

Test a valid job, invalid input, unauthorized access, a repeated key, conflicting key reuse, a timeout after acceptance, a failed job, and expired result retention. Confirm account isolation and the final artifact. A static OpenAPI review can identify missing contracts; only execution against an authorized service can verify the implementation.

## Sources

- [IETF RFC 9457: Problem details for HTTP APIs](https://www.rfc-editor.org/rfc/rfc9457.html)

## Related reading

- [API documentation an agent can actually use](https://byagents.dev/humans/api-documentation)
- [Reference: identity, authorization, and untrusted content](https://byagents.dev/agents/authorization-and-trust)
- [Template: a reproducible agent evaluation record](https://byagents.dev/agents/evaluation-record-template)
