# API Reference

Base path: `/v1/awe/`. The spec below is rendered from the live [`docs/openapi.json`](https://github.com/OpenG2P/awe/blob/develop/docs/openapi.json). CI regenerates it from the FastAPI app on every `src/`-touching push, so endpoint signatures, response shapes, status-code descriptions, and the error-code catalog stay in lockstep with the code. This page does **not** duplicate any of that in prose.

A running instance also exposes the live spec at `/v1/awe/openapi.json` and interactive UIs at `/v1/awe/docs` (Swagger) and `/v1/awe/redoc`.

## Caller API surface — what an integrator actually uses

The full reference below covers every endpoint, including the admin surface that powers AWE's own portal (policy CRUD, simulate, delegations, audit log, delivery retry). **Caller services do not call any of those.**

A Caller integration touches only the five endpoints below, plus implements one inbound webhook handler.

### Outbound — Caller → AWE

| API                                                                       | When the Caller calls it                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `POST /v1/awe/requests`                                                   | A Caller-owned artifact has been created and needs approval. Pass `policy_key`, `artifact_type`, `artifact_id`, `context`, `callback_url`, `requester`.                                                                                                                                                                                                                                                                                                                                     |
| `POST /v1/awe/requests/{id}/cancel`                                       | The underlying artifact was withdrawn, or the Caller wants to abort an in-flight flow.                                                                                                                                                                                                                                                                                                                                                                                                      |
| `GET /v1/awe/tasks?assignee=me` *(forwarding the approver's JWT)*         | When an approver opens the Caller's UI — returns every open AWE task assigned to the user whose JWT is on the request, across all requests and policies. `me` expands to the token's `sub` claim. The Caller joins this list with its own artifact rows (by `awe_request_id`) to build the per-Caller inbox.                                                                                                                                                                                |
| `POST /v1/awe/tasks/{task_id}/decision` *(forwarding the approver's JWT)* | Records the approver's `approve` / `reject` / `abstain`.                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `GET /v1/awe/requests/{id}` *(optional)*                                  | Per-request lookup — full state of one approval flow by id (current stage, resolved approvers, history, context snapshot). Used when a user opens an artifact detail page in the Caller's UI and the Caller wants to render an "approval state" panel inline. **Not** an admin list view; takes a single id, returns one request. Skip it if you'd rather rely solely on the webhook to drive UI state. The Caller decides which users see this panel — AWE itself does not restrict reads. |

### Inbound — AWE → Caller (implemented by the Caller, not called)

| Endpoint              | What the Caller does                                                                                                                                                                                                                                                                    |
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `POST {callback_url}` | Receives signed AWE webhooks. On `request_approved` apply the artifact-side effect; on `request_rejected` / `request_cancelled` close out the artifact accordingly. Validate the `X-Approval-Signature` HMAC, dedup on `X-Approval-Event-Id`, return 2xx within the configured timeout. |

Everything else in the reference below is admin / operator surface served from AWE's bundled portal.

### Error responses

Every non-2xx response returns AWE's standard error envelope with an `AWE-NNN` code in `errors[0].errorCode`. See the [Error codes](/platform/platform-services/approval-workflow-engine/error-codes.md) page for the full catalog — what each code means, the HTTP status it ships with, and whether to retry.

***

## GET /v1/awe/health

> Health / readiness probe

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"tags":[{"name":"health","description":"Service-level endpoints — liveness/readiness probe, build metadata, effective non-sensitive configuration. Unauthenticated."}],"paths":{"/v1/awe/health":{"get":{"tags":["health"],"summary":"Health / readiness probe","operationId":"health_v1_awe_health_get","responses":{"200":{"description":"Service is ready.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}},"503":{"description":"Health probe failed to reach the database.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"components":{"schemas":{"HealthResponse":{"properties":{"id":{"type":"string","title":"Id","default":"openg2p.awe"},"version":{"type":"string","title":"Version","default":"1.0"},"responsetime":{"type":"string","title":"Responsetime"},"response":{"$ref":"#/components/schemas/HealthPayload"},"errors":{"items":{"$ref":"#/components/schemas/ErrorDetail"},"type":"array","title":"Errors"}},"type":"object","required":["responsetime","response"],"title":"HealthResponse"},"HealthPayload":{"properties":{"status":{"type":"string","title":"Status"}},"type":"object","required":["status"],"title":"HealthPayload"},"ErrorDetail":{"properties":{"errorCode":{"type":"string","title":"Errorcode","description":"OpenG2P-assigned error code. AWE catalog: `AWE-001` (policy not found), `AWE-002` (policy conflict / version clash), `AWE-003` (request not found), `AWE-004` (task not found), `AWE-005` (service not ready — startup incomplete), `AWE-006` (database health check failed), `AWE-007` (invalid state transition), `AWE-008` (unauthorized / forbidden), `AWE-009` (idempotency key conflict with different payload), `AWE-010` (validation — bad policy definition)."},"message":{"type":"string","title":"Message"}},"type":"object","required":["errorCode","message"],"title":"ErrorDetail"},"ErrorResponse":{"properties":{"id":{"type":"string","title":"Id","default":"openg2p.awe"},"version":{"type":"string","title":"Version","default":"1.0"},"responsetime":{"type":"string","title":"Responsetime"},"response":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Response"},"errors":{"items":{"$ref":"#/components/schemas/ErrorDetail"},"type":"array","title":"Errors"}},"type":"object","required":["responsetime","errors"],"title":"ErrorResponse"}}}}
```

## GET /v1/awe/version

> Service version + build metadata

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"tags":[{"name":"health","description":"Service-level endpoints — liveness/readiness probe, build metadata, effective non-sensitive configuration. Unauthenticated."}],"paths":{"/v1/awe/version":{"get":{"tags":["health"],"summary":"Service version + build metadata","operationId":"version_v1_awe_version_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VersionResponse"}}}}}}}},"components":{"schemas":{"VersionResponse":{"properties":{"id":{"type":"string","title":"Id","default":"openg2p.awe"},"version":{"type":"string","title":"Version","default":"1.0"},"responsetime":{"type":"string","title":"Responsetime"},"response":{"$ref":"#/components/schemas/VersionPayload"},"errors":{"items":{"$ref":"#/components/schemas/ErrorDetail"},"type":"array","title":"Errors"}},"type":"object","required":["responsetime","response"],"title":"VersionResponse"},"VersionPayload":{"properties":{"service_version":{"type":"string","title":"Service Version"},"build_time":{"type":"string","title":"Build Time"},"git_commit":{"type":"string","title":"Git Commit"}},"type":"object","required":["service_version","build_time","git_commit"],"title":"VersionPayload"},"ErrorDetail":{"properties":{"errorCode":{"type":"string","title":"Errorcode","description":"OpenG2P-assigned error code. AWE catalog: `AWE-001` (policy not found), `AWE-002` (policy conflict / version clash), `AWE-003` (request not found), `AWE-004` (task not found), `AWE-005` (service not ready — startup incomplete), `AWE-006` (database health check failed), `AWE-007` (invalid state transition), `AWE-008` (unauthorized / forbidden), `AWE-009` (idempotency key conflict with different payload), `AWE-010` (validation — bad policy definition)."},"message":{"type":"string","title":"Message"}},"type":"object","required":["errorCode","message"],"title":"ErrorDetail"}}}}
```

## GET /v1/awe/config

> Effective non-sensitive configuration

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"tags":[{"name":"health","description":"Service-level endpoints — liveness/readiness probe, build metadata, effective non-sensitive configuration. Unauthenticated."}],"paths":{"/v1/awe/config":{"get":{"tags":["health"],"summary":"Effective non-sensitive configuration","operationId":"config_view_v1_awe_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}}}
```

## POST /v1/awe/policies

> Create the first draft of a new policy

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"tags":[{"name":"policies","description":"Policy CRUD, versioning, activation, and simulation. **Admin surface only** — used by the bundled admin SPA and GitOps tooling. Caller services should never call these endpoints."}],"security":[{"HTTPBearer":[]}],"components":{"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"}},"schemas":{"PolicyCreate":{"properties":{"policy_key":{"type":"string","title":"Policy Key"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"artifact_type":{"type":"string","title":"Artifact Type"},"forbid_self_approval":{"type":"boolean","title":"Forbid Self Approval","description":"Filter the request's requester out of every stage's approver list.","default":false},"forbid_repeat_approvers":{"type":"boolean","title":"Forbid Repeat Approvers","description":"Filter users who approved an earlier stage out of later stages.","default":false},"stages":{"items":{"$ref":"#/components/schemas/StageIn"},"type":"array","title":"Stages"}},"type":"object","required":["policy_key","name","artifact_type"],"title":"PolicyCreate"},"StageIn":{"properties":{"name":{"type":"string","title":"Name"},"stage_order":{"type":"integer","minimum":1,"title":"Stage Order"},"mode":{"type":"string","title":"Mode","default":"all"},"mode_value":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"title":"Mode Value"},"sla_hours":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"title":"Sla Hours"},"skip_if":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Skip If"},"on_empty":{"type":"string","title":"On Empty","default":"block"},"parallel_group":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Parallel Group","description":"Stages sharing a parallel_group run concurrently and must all approve before the group completes. Null = stage is its own group (strictly sequential)."},"on_breach":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"On Breach","description":"What to do when every open task in the stage has crossed its due_at: 'notify' (default behaviour) / 'auto_approve' / 'auto_reject' / 'escalate'."},"escalation_rules":{"items":{"$ref":"#/components/schemas/ApproverRuleIn"},"type":"array","title":"Escalation Rules","description":"Rules resolved to add fresh approvers when on_breach='escalate'."},"rules":{"items":{"$ref":"#/components/schemas/ApproverRuleIn"},"type":"array","title":"Rules"}},"type":"object","required":["name","stage_order"],"title":"StageIn"},"ApproverRuleIn":{"properties":{"rule_type":{"type":"string","title":"Rule Type"},"rule_value":{"additionalProperties":true,"type":"object","title":"Rule Value"},"kind":{"type":"string","title":"Kind","description":"'approver' counts toward stage completion; 'observer' gets a comment-only task.","default":"approver"},"required":{"type":"boolean","title":"Required","description":"If true, every user resolved by this rule must approve (overrides quorum).","default":false}},"type":"object","required":["rule_type","rule_value"],"title":"ApproverRuleIn"},"PolicyOut":{"properties":{"id":{"type":"string","title":"Id"},"policy_key":{"type":"string","title":"Policy Key"},"version":{"type":"integer","title":"Version"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"status":{"type":"string","title":"Status"},"artifact_type":{"type":"string","title":"Artifact Type"},"forbid_self_approval":{"type":"boolean","title":"Forbid Self Approval","default":false},"forbid_repeat_approvers":{"type":"boolean","title":"Forbid Repeat Approvers","default":false},"created_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"stages":{"items":{"$ref":"#/components/schemas/StageOut"},"type":"array","title":"Stages"}},"type":"object","required":["id","policy_key","version","name","status","artifact_type","created_at","updated_at"],"title":"PolicyOut"},"StageOut":{"properties":{"name":{"type":"string","title":"Name"},"stage_order":{"type":"integer","minimum":1,"title":"Stage Order"},"mode":{"type":"string","title":"Mode","default":"all"},"mode_value":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"title":"Mode Value"},"sla_hours":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"title":"Sla Hours"},"skip_if":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Skip If"},"on_empty":{"type":"string","title":"On Empty","default":"block"},"parallel_group":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Parallel Group","description":"Stages sharing a parallel_group run concurrently and must all approve before the group completes. Null = stage is its own group (strictly sequential)."},"on_breach":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"On Breach","description":"What to do when every open task in the stage has crossed its due_at: 'notify' (default behaviour) / 'auto_approve' / 'auto_reject' / 'escalate'."},"escalation_rules":{"items":{"$ref":"#/components/schemas/ApproverRuleOut"},"type":"array","title":"Escalation Rules"},"rules":{"items":{"$ref":"#/components/schemas/ApproverRuleOut"},"type":"array","title":"Rules"},"id":{"type":"string","title":"Id"}},"type":"object","required":["name","stage_order","id"],"title":"StageOut"},"ApproverRuleOut":{"properties":{"rule_type":{"type":"string","title":"Rule Type"},"rule_value":{"additionalProperties":true,"type":"object","title":"Rule Value"},"kind":{"type":"string","title":"Kind","description":"'approver' counts toward stage completion; 'observer' gets a comment-only task.","default":"approver"},"required":{"type":"boolean","title":"Required","description":"If true, every user resolved by this rule must approve (overrides quorum).","default":false},"id":{"type":"string","title":"Id"}},"type":"object","required":["rule_type","rule_value","id"],"title":"ApproverRuleOut"},"ErrorResponse":{"properties":{"id":{"type":"string","title":"Id","default":"openg2p.awe"},"version":{"type":"string","title":"Version","default":"1.0"},"responsetime":{"type":"string","title":"Responsetime"},"response":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Response"},"errors":{"items":{"$ref":"#/components/schemas/ErrorDetail"},"type":"array","title":"Errors"}},"type":"object","required":["responsetime","errors"],"title":"ErrorResponse"},"ErrorDetail":{"properties":{"errorCode":{"type":"string","title":"Errorcode","description":"OpenG2P-assigned error code. AWE catalog: `AWE-001` (policy not found), `AWE-002` (policy conflict / version clash), `AWE-003` (request not found), `AWE-004` (task not found), `AWE-005` (service not ready — startup incomplete), `AWE-006` (database health check failed), `AWE-007` (invalid state transition), `AWE-008` (unauthorized / forbidden), `AWE-009` (idempotency key conflict with different payload), `AWE-010` (validation — bad policy definition)."},"message":{"type":"string","title":"Message"}},"type":"object","required":["errorCode","message"],"title":"ErrorDetail"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}},"paths":{"/v1/awe/policies":{"post":{"tags":["policies"],"summary":"Create the first draft of a new policy","operationId":"create_policy_v1_awe_policies_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PolicyCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PolicyOut"}}}},"401":{"description":"Bearer token missing, malformed, or fails signature/expiry checks.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Token is valid but lacks the `AWE_ADMIN` role required for this mutation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Policy version conflict — typically a duplicate `policy_key` on create, or attempting to edit/activate a version in the wrong status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}}}
```

## GET /v1/awe/policies

> List policies (newest version of each policy\_key)

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"tags":[{"name":"policies","description":"Policy CRUD, versioning, activation, and simulation. **Admin surface only** — used by the bundled admin SPA and GitOps tooling. Caller services should never call these endpoints."}],"security":[{"HTTPBearer":[]}],"components":{"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"}},"schemas":{"PolicyOut":{"properties":{"id":{"type":"string","title":"Id"},"policy_key":{"type":"string","title":"Policy Key"},"version":{"type":"integer","title":"Version"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"status":{"type":"string","title":"Status"},"artifact_type":{"type":"string","title":"Artifact Type"},"forbid_self_approval":{"type":"boolean","title":"Forbid Self Approval","default":false},"forbid_repeat_approvers":{"type":"boolean","title":"Forbid Repeat Approvers","default":false},"created_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"stages":{"items":{"$ref":"#/components/schemas/StageOut"},"type":"array","title":"Stages"}},"type":"object","required":["id","policy_key","version","name","status","artifact_type","created_at","updated_at"],"title":"PolicyOut"},"StageOut":{"properties":{"name":{"type":"string","title":"Name"},"stage_order":{"type":"integer","minimum":1,"title":"Stage Order"},"mode":{"type":"string","title":"Mode","default":"all"},"mode_value":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"title":"Mode Value"},"sla_hours":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"title":"Sla Hours"},"skip_if":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Skip If"},"on_empty":{"type":"string","title":"On Empty","default":"block"},"parallel_group":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Parallel Group","description":"Stages sharing a parallel_group run concurrently and must all approve before the group completes. Null = stage is its own group (strictly sequential)."},"on_breach":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"On Breach","description":"What to do when every open task in the stage has crossed its due_at: 'notify' (default behaviour) / 'auto_approve' / 'auto_reject' / 'escalate'."},"escalation_rules":{"items":{"$ref":"#/components/schemas/ApproverRuleOut"},"type":"array","title":"Escalation Rules"},"rules":{"items":{"$ref":"#/components/schemas/ApproverRuleOut"},"type":"array","title":"Rules"},"id":{"type":"string","title":"Id"}},"type":"object","required":["name","stage_order","id"],"title":"StageOut"},"ApproverRuleOut":{"properties":{"rule_type":{"type":"string","title":"Rule Type"},"rule_value":{"additionalProperties":true,"type":"object","title":"Rule Value"},"kind":{"type":"string","title":"Kind","description":"'approver' counts toward stage completion; 'observer' gets a comment-only task.","default":"approver"},"required":{"type":"boolean","title":"Required","description":"If true, every user resolved by this rule must approve (overrides quorum).","default":false},"id":{"type":"string","title":"Id"}},"type":"object","required":["rule_type","rule_value","id"],"title":"ApproverRuleOut"},"ErrorResponse":{"properties":{"id":{"type":"string","title":"Id","default":"openg2p.awe"},"version":{"type":"string","title":"Version","default":"1.0"},"responsetime":{"type":"string","title":"Responsetime"},"response":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Response"},"errors":{"items":{"$ref":"#/components/schemas/ErrorDetail"},"type":"array","title":"Errors"}},"type":"object","required":["responsetime","errors"],"title":"ErrorResponse"},"ErrorDetail":{"properties":{"errorCode":{"type":"string","title":"Errorcode","description":"OpenG2P-assigned error code. AWE catalog: `AWE-001` (policy not found), `AWE-002` (policy conflict / version clash), `AWE-003` (request not found), `AWE-004` (task not found), `AWE-005` (service not ready — startup incomplete), `AWE-006` (database health check failed), `AWE-007` (invalid state transition), `AWE-008` (unauthorized / forbidden), `AWE-009` (idempotency key conflict with different payload), `AWE-010` (validation — bad policy definition)."},"message":{"type":"string","title":"Message"}},"type":"object","required":["errorCode","message"],"title":"ErrorDetail"}}},"paths":{"/v1/awe/policies":{"get":{"tags":["policies"],"summary":"List policies (newest version of each policy_key)","operationId":"list_policies_v1_awe_policies_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PolicyOut"},"type":"array","title":"Response List Policies V1 Awe Policies Get"}}}},"401":{"description":"Bearer token missing, malformed, or fails signature/expiry checks.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Token is valid but lacks `AWE_VIEWER` or `AWE_ADMIN` role.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}}}
```

## GET /v1/awe/policies/{policy\_key}/versions

> List all versions of a policy\_key

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"tags":[{"name":"policies","description":"Policy CRUD, versioning, activation, and simulation. **Admin surface only** — used by the bundled admin SPA and GitOps tooling. Caller services should never call these endpoints."}],"security":[{"HTTPBearer":[]}],"components":{"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"}},"schemas":{"PolicyVersionOut":{"properties":{"id":{"type":"string","title":"Id"},"policy_key":{"type":"string","title":"Policy Key"},"version":{"type":"integer","title":"Version"},"status":{"type":"string","title":"Status"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","policy_key","version","status","created_at"],"title":"PolicyVersionOut"},"ErrorResponse":{"properties":{"id":{"type":"string","title":"Id","default":"openg2p.awe"},"version":{"type":"string","title":"Version","default":"1.0"},"responsetime":{"type":"string","title":"Responsetime"},"response":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Response"},"errors":{"items":{"$ref":"#/components/schemas/ErrorDetail"},"type":"array","title":"Errors"}},"type":"object","required":["responsetime","errors"],"title":"ErrorResponse"},"ErrorDetail":{"properties":{"errorCode":{"type":"string","title":"Errorcode","description":"OpenG2P-assigned error code. AWE catalog: `AWE-001` (policy not found), `AWE-002` (policy conflict / version clash), `AWE-003` (request not found), `AWE-004` (task not found), `AWE-005` (service not ready — startup incomplete), `AWE-006` (database health check failed), `AWE-007` (invalid state transition), `AWE-008` (unauthorized / forbidden), `AWE-009` (idempotency key conflict with different payload), `AWE-010` (validation — bad policy definition)."},"message":{"type":"string","title":"Message"}},"type":"object","required":["errorCode","message"],"title":"ErrorDetail"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}},"paths":{"/v1/awe/policies/{policy_key}/versions":{"get":{"tags":["policies"],"summary":"List all versions of a policy_key","operationId":"list_versions_v1_awe_policies__policy_key__versions_get","parameters":[{"name":"policy_key","in":"path","required":true,"schema":{"type":"string","title":"Policy Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PolicyVersionOut"},"title":"Response List Versions V1 Awe Policies  Policy Key  Versions Get"}}}},"401":{"description":"Bearer token missing, malformed, or fails signature/expiry checks.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Token is valid but lacks `AWE_VIEWER` or `AWE_ADMIN` role.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No matching policy / policy version exists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}}}
```

## GET /v1/awe/policies/{policy\_key}/versions/{version}

> Fetch a specific policy version with stages and rules

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"tags":[{"name":"policies","description":"Policy CRUD, versioning, activation, and simulation. **Admin surface only** — used by the bundled admin SPA and GitOps tooling. Caller services should never call these endpoints."}],"security":[{"HTTPBearer":[]}],"components":{"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"}},"schemas":{"PolicyOut":{"properties":{"id":{"type":"string","title":"Id"},"policy_key":{"type":"string","title":"Policy Key"},"version":{"type":"integer","title":"Version"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"status":{"type":"string","title":"Status"},"artifact_type":{"type":"string","title":"Artifact Type"},"forbid_self_approval":{"type":"boolean","title":"Forbid Self Approval","default":false},"forbid_repeat_approvers":{"type":"boolean","title":"Forbid Repeat Approvers","default":false},"created_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"stages":{"items":{"$ref":"#/components/schemas/StageOut"},"type":"array","title":"Stages"}},"type":"object","required":["id","policy_key","version","name","status","artifact_type","created_at","updated_at"],"title":"PolicyOut"},"StageOut":{"properties":{"name":{"type":"string","title":"Name"},"stage_order":{"type":"integer","minimum":1,"title":"Stage Order"},"mode":{"type":"string","title":"Mode","default":"all"},"mode_value":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"title":"Mode Value"},"sla_hours":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"title":"Sla Hours"},"skip_if":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Skip If"},"on_empty":{"type":"string","title":"On Empty","default":"block"},"parallel_group":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Parallel Group","description":"Stages sharing a parallel_group run concurrently and must all approve before the group completes. Null = stage is its own group (strictly sequential)."},"on_breach":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"On Breach","description":"What to do when every open task in the stage has crossed its due_at: 'notify' (default behaviour) / 'auto_approve' / 'auto_reject' / 'escalate'."},"escalation_rules":{"items":{"$ref":"#/components/schemas/ApproverRuleOut"},"type":"array","title":"Escalation Rules"},"rules":{"items":{"$ref":"#/components/schemas/ApproverRuleOut"},"type":"array","title":"Rules"},"id":{"type":"string","title":"Id"}},"type":"object","required":["name","stage_order","id"],"title":"StageOut"},"ApproverRuleOut":{"properties":{"rule_type":{"type":"string","title":"Rule Type"},"rule_value":{"additionalProperties":true,"type":"object","title":"Rule Value"},"kind":{"type":"string","title":"Kind","description":"'approver' counts toward stage completion; 'observer' gets a comment-only task.","default":"approver"},"required":{"type":"boolean","title":"Required","description":"If true, every user resolved by this rule must approve (overrides quorum).","default":false},"id":{"type":"string","title":"Id"}},"type":"object","required":["rule_type","rule_value","id"],"title":"ApproverRuleOut"},"ErrorResponse":{"properties":{"id":{"type":"string","title":"Id","default":"openg2p.awe"},"version":{"type":"string","title":"Version","default":"1.0"},"responsetime":{"type":"string","title":"Responsetime"},"response":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Response"},"errors":{"items":{"$ref":"#/components/schemas/ErrorDetail"},"type":"array","title":"Errors"}},"type":"object","required":["responsetime","errors"],"title":"ErrorResponse"},"ErrorDetail":{"properties":{"errorCode":{"type":"string","title":"Errorcode","description":"OpenG2P-assigned error code. AWE catalog: `AWE-001` (policy not found), `AWE-002` (policy conflict / version clash), `AWE-003` (request not found), `AWE-004` (task not found), `AWE-005` (service not ready — startup incomplete), `AWE-006` (database health check failed), `AWE-007` (invalid state transition), `AWE-008` (unauthorized / forbidden), `AWE-009` (idempotency key conflict with different payload), `AWE-010` (validation — bad policy definition)."},"message":{"type":"string","title":"Message"}},"type":"object","required":["errorCode","message"],"title":"ErrorDetail"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}},"paths":{"/v1/awe/policies/{policy_key}/versions/{version}":{"get":{"tags":["policies"],"summary":"Fetch a specific policy version with stages and rules","operationId":"get_version_v1_awe_policies__policy_key__versions__version__get","parameters":[{"name":"policy_key","in":"path","required":true,"schema":{"type":"string","title":"Policy Key"}},{"name":"version","in":"path","required":true,"schema":{"type":"integer","title":"Version"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PolicyOut"}}}},"401":{"description":"Bearer token missing, malformed, or fails signature/expiry checks.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Token is valid but lacks `AWE_VIEWER` or `AWE_ADMIN` role.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No matching policy / policy version exists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}}}
```

## PATCH /v1/awe/policies/{policy\_key}/versions/{version}

> Edit a draft version in place (drafts only)

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"tags":[{"name":"policies","description":"Policy CRUD, versioning, activation, and simulation. **Admin surface only** — used by the bundled admin SPA and GitOps tooling. Caller services should never call these endpoints."}],"security":[{"HTTPBearer":[]}],"components":{"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"}},"schemas":{"PolicyCreate":{"properties":{"policy_key":{"type":"string","title":"Policy Key"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"artifact_type":{"type":"string","title":"Artifact Type"},"forbid_self_approval":{"type":"boolean","title":"Forbid Self Approval","description":"Filter the request's requester out of every stage's approver list.","default":false},"forbid_repeat_approvers":{"type":"boolean","title":"Forbid Repeat Approvers","description":"Filter users who approved an earlier stage out of later stages.","default":false},"stages":{"items":{"$ref":"#/components/schemas/StageIn"},"type":"array","title":"Stages"}},"type":"object","required":["policy_key","name","artifact_type"],"title":"PolicyCreate"},"StageIn":{"properties":{"name":{"type":"string","title":"Name"},"stage_order":{"type":"integer","minimum":1,"title":"Stage Order"},"mode":{"type":"string","title":"Mode","default":"all"},"mode_value":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"title":"Mode Value"},"sla_hours":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"title":"Sla Hours"},"skip_if":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Skip If"},"on_empty":{"type":"string","title":"On Empty","default":"block"},"parallel_group":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Parallel Group","description":"Stages sharing a parallel_group run concurrently and must all approve before the group completes. Null = stage is its own group (strictly sequential)."},"on_breach":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"On Breach","description":"What to do when every open task in the stage has crossed its due_at: 'notify' (default behaviour) / 'auto_approve' / 'auto_reject' / 'escalate'."},"escalation_rules":{"items":{"$ref":"#/components/schemas/ApproverRuleIn"},"type":"array","title":"Escalation Rules","description":"Rules resolved to add fresh approvers when on_breach='escalate'."},"rules":{"items":{"$ref":"#/components/schemas/ApproverRuleIn"},"type":"array","title":"Rules"}},"type":"object","required":["name","stage_order"],"title":"StageIn"},"ApproverRuleIn":{"properties":{"rule_type":{"type":"string","title":"Rule Type"},"rule_value":{"additionalProperties":true,"type":"object","title":"Rule Value"},"kind":{"type":"string","title":"Kind","description":"'approver' counts toward stage completion; 'observer' gets a comment-only task.","default":"approver"},"required":{"type":"boolean","title":"Required","description":"If true, every user resolved by this rule must approve (overrides quorum).","default":false}},"type":"object","required":["rule_type","rule_value"],"title":"ApproverRuleIn"},"PolicyOut":{"properties":{"id":{"type":"string","title":"Id"},"policy_key":{"type":"string","title":"Policy Key"},"version":{"type":"integer","title":"Version"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"status":{"type":"string","title":"Status"},"artifact_type":{"type":"string","title":"Artifact Type"},"forbid_self_approval":{"type":"boolean","title":"Forbid Self Approval","default":false},"forbid_repeat_approvers":{"type":"boolean","title":"Forbid Repeat Approvers","default":false},"created_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"stages":{"items":{"$ref":"#/components/schemas/StageOut"},"type":"array","title":"Stages"}},"type":"object","required":["id","policy_key","version","name","status","artifact_type","created_at","updated_at"],"title":"PolicyOut"},"StageOut":{"properties":{"name":{"type":"string","title":"Name"},"stage_order":{"type":"integer","minimum":1,"title":"Stage Order"},"mode":{"type":"string","title":"Mode","default":"all"},"mode_value":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"title":"Mode Value"},"sla_hours":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"title":"Sla Hours"},"skip_if":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Skip If"},"on_empty":{"type":"string","title":"On Empty","default":"block"},"parallel_group":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Parallel Group","description":"Stages sharing a parallel_group run concurrently and must all approve before the group completes. Null = stage is its own group (strictly sequential)."},"on_breach":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"On Breach","description":"What to do when every open task in the stage has crossed its due_at: 'notify' (default behaviour) / 'auto_approve' / 'auto_reject' / 'escalate'."},"escalation_rules":{"items":{"$ref":"#/components/schemas/ApproverRuleOut"},"type":"array","title":"Escalation Rules"},"rules":{"items":{"$ref":"#/components/schemas/ApproverRuleOut"},"type":"array","title":"Rules"},"id":{"type":"string","title":"Id"}},"type":"object","required":["name","stage_order","id"],"title":"StageOut"},"ApproverRuleOut":{"properties":{"rule_type":{"type":"string","title":"Rule Type"},"rule_value":{"additionalProperties":true,"type":"object","title":"Rule Value"},"kind":{"type":"string","title":"Kind","description":"'approver' counts toward stage completion; 'observer' gets a comment-only task.","default":"approver"},"required":{"type":"boolean","title":"Required","description":"If true, every user resolved by this rule must approve (overrides quorum).","default":false},"id":{"type":"string","title":"Id"}},"type":"object","required":["rule_type","rule_value","id"],"title":"ApproverRuleOut"},"ErrorResponse":{"properties":{"id":{"type":"string","title":"Id","default":"openg2p.awe"},"version":{"type":"string","title":"Version","default":"1.0"},"responsetime":{"type":"string","title":"Responsetime"},"response":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Response"},"errors":{"items":{"$ref":"#/components/schemas/ErrorDetail"},"type":"array","title":"Errors"}},"type":"object","required":["responsetime","errors"],"title":"ErrorResponse"},"ErrorDetail":{"properties":{"errorCode":{"type":"string","title":"Errorcode","description":"OpenG2P-assigned error code. AWE catalog: `AWE-001` (policy not found), `AWE-002` (policy conflict / version clash), `AWE-003` (request not found), `AWE-004` (task not found), `AWE-005` (service not ready — startup incomplete), `AWE-006` (database health check failed), `AWE-007` (invalid state transition), `AWE-008` (unauthorized / forbidden), `AWE-009` (idempotency key conflict with different payload), `AWE-010` (validation — bad policy definition)."},"message":{"type":"string","title":"Message"}},"type":"object","required":["errorCode","message"],"title":"ErrorDetail"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}},"paths":{"/v1/awe/policies/{policy_key}/versions/{version}":{"patch":{"tags":["policies"],"summary":"Edit a draft version in place (drafts only)","operationId":"edit_draft_v1_awe_policies__policy_key__versions__version__patch","parameters":[{"name":"policy_key","in":"path","required":true,"schema":{"type":"string","title":"Policy Key"}},{"name":"version","in":"path","required":true,"schema":{"type":"integer","title":"Version"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PolicyCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PolicyOut"}}}},"400":{"description":"Policy body violates a structural rule (e.g. URL/body key mismatch).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Bearer token missing, malformed, or fails signature/expiry checks.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Token is valid but lacks the `AWE_ADMIN` role required for this mutation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No matching policy / policy version exists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Resource is in a state that disallows this transition. Examples: request is already in a terminal state (`approved`/`rejected`/`cancelled`); task is not `open`/`claimed`; policy version is not `draft` for editing; delivery has already succeeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}}}
```

## POST /v1/awe/policies/{policy\_key}/versions/{version}/activate

> Activate a specific version (archives the previously active one)

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"tags":[{"name":"policies","description":"Policy CRUD, versioning, activation, and simulation. **Admin surface only** — used by the bundled admin SPA and GitOps tooling. Caller services should never call these endpoints."}],"security":[{"HTTPBearer":[]}],"components":{"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"}},"schemas":{"PolicyOut":{"properties":{"id":{"type":"string","title":"Id"},"policy_key":{"type":"string","title":"Policy Key"},"version":{"type":"integer","title":"Version"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"status":{"type":"string","title":"Status"},"artifact_type":{"type":"string","title":"Artifact Type"},"forbid_self_approval":{"type":"boolean","title":"Forbid Self Approval","default":false},"forbid_repeat_approvers":{"type":"boolean","title":"Forbid Repeat Approvers","default":false},"created_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"stages":{"items":{"$ref":"#/components/schemas/StageOut"},"type":"array","title":"Stages"}},"type":"object","required":["id","policy_key","version","name","status","artifact_type","created_at","updated_at"],"title":"PolicyOut"},"StageOut":{"properties":{"name":{"type":"string","title":"Name"},"stage_order":{"type":"integer","minimum":1,"title":"Stage Order"},"mode":{"type":"string","title":"Mode","default":"all"},"mode_value":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"title":"Mode Value"},"sla_hours":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"title":"Sla Hours"},"skip_if":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Skip If"},"on_empty":{"type":"string","title":"On Empty","default":"block"},"parallel_group":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Parallel Group","description":"Stages sharing a parallel_group run concurrently and must all approve before the group completes. Null = stage is its own group (strictly sequential)."},"on_breach":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"On Breach","description":"What to do when every open task in the stage has crossed its due_at: 'notify' (default behaviour) / 'auto_approve' / 'auto_reject' / 'escalate'."},"escalation_rules":{"items":{"$ref":"#/components/schemas/ApproverRuleOut"},"type":"array","title":"Escalation Rules"},"rules":{"items":{"$ref":"#/components/schemas/ApproverRuleOut"},"type":"array","title":"Rules"},"id":{"type":"string","title":"Id"}},"type":"object","required":["name","stage_order","id"],"title":"StageOut"},"ApproverRuleOut":{"properties":{"rule_type":{"type":"string","title":"Rule Type"},"rule_value":{"additionalProperties":true,"type":"object","title":"Rule Value"},"kind":{"type":"string","title":"Kind","description":"'approver' counts toward stage completion; 'observer' gets a comment-only task.","default":"approver"},"required":{"type":"boolean","title":"Required","description":"If true, every user resolved by this rule must approve (overrides quorum).","default":false},"id":{"type":"string","title":"Id"}},"type":"object","required":["rule_type","rule_value","id"],"title":"ApproverRuleOut"},"ErrorResponse":{"properties":{"id":{"type":"string","title":"Id","default":"openg2p.awe"},"version":{"type":"string","title":"Version","default":"1.0"},"responsetime":{"type":"string","title":"Responsetime"},"response":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Response"},"errors":{"items":{"$ref":"#/components/schemas/ErrorDetail"},"type":"array","title":"Errors"}},"type":"object","required":["responsetime","errors"],"title":"ErrorResponse"},"ErrorDetail":{"properties":{"errorCode":{"type":"string","title":"Errorcode","description":"OpenG2P-assigned error code. AWE catalog: `AWE-001` (policy not found), `AWE-002` (policy conflict / version clash), `AWE-003` (request not found), `AWE-004` (task not found), `AWE-005` (service not ready — startup incomplete), `AWE-006` (database health check failed), `AWE-007` (invalid state transition), `AWE-008` (unauthorized / forbidden), `AWE-009` (idempotency key conflict with different payload), `AWE-010` (validation — bad policy definition)."},"message":{"type":"string","title":"Message"}},"type":"object","required":["errorCode","message"],"title":"ErrorDetail"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}},"paths":{"/v1/awe/policies/{policy_key}/versions/{version}/activate":{"post":{"tags":["policies"],"summary":"Activate a specific version (archives the previously active one)","operationId":"activate_v1_awe_policies__policy_key__versions__version__activate_post","parameters":[{"name":"policy_key","in":"path","required":true,"schema":{"type":"string","title":"Policy Key"}},{"name":"version","in":"path","required":true,"schema":{"type":"integer","title":"Version"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PolicyOut"}}}},"401":{"description":"Bearer token missing, malformed, or fails signature/expiry checks.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Token is valid but lacks the `AWE_ADMIN` role required for this mutation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No matching policy / policy version exists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}}}
```

## PUT /v1/awe/policies/{policy\_key}

> Add a new draft version under an existing policy\_key

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"tags":[{"name":"policies","description":"Policy CRUD, versioning, activation, and simulation. **Admin surface only** — used by the bundled admin SPA and GitOps tooling. Caller services should never call these endpoints."}],"security":[{"HTTPBearer":[]}],"components":{"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"}},"schemas":{"PolicyCreate":{"properties":{"policy_key":{"type":"string","title":"Policy Key"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"artifact_type":{"type":"string","title":"Artifact Type"},"forbid_self_approval":{"type":"boolean","title":"Forbid Self Approval","description":"Filter the request's requester out of every stage's approver list.","default":false},"forbid_repeat_approvers":{"type":"boolean","title":"Forbid Repeat Approvers","description":"Filter users who approved an earlier stage out of later stages.","default":false},"stages":{"items":{"$ref":"#/components/schemas/StageIn"},"type":"array","title":"Stages"}},"type":"object","required":["policy_key","name","artifact_type"],"title":"PolicyCreate"},"StageIn":{"properties":{"name":{"type":"string","title":"Name"},"stage_order":{"type":"integer","minimum":1,"title":"Stage Order"},"mode":{"type":"string","title":"Mode","default":"all"},"mode_value":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"title":"Mode Value"},"sla_hours":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"title":"Sla Hours"},"skip_if":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Skip If"},"on_empty":{"type":"string","title":"On Empty","default":"block"},"parallel_group":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Parallel Group","description":"Stages sharing a parallel_group run concurrently and must all approve before the group completes. Null = stage is its own group (strictly sequential)."},"on_breach":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"On Breach","description":"What to do when every open task in the stage has crossed its due_at: 'notify' (default behaviour) / 'auto_approve' / 'auto_reject' / 'escalate'."},"escalation_rules":{"items":{"$ref":"#/components/schemas/ApproverRuleIn"},"type":"array","title":"Escalation Rules","description":"Rules resolved to add fresh approvers when on_breach='escalate'."},"rules":{"items":{"$ref":"#/components/schemas/ApproverRuleIn"},"type":"array","title":"Rules"}},"type":"object","required":["name","stage_order"],"title":"StageIn"},"ApproverRuleIn":{"properties":{"rule_type":{"type":"string","title":"Rule Type"},"rule_value":{"additionalProperties":true,"type":"object","title":"Rule Value"},"kind":{"type":"string","title":"Kind","description":"'approver' counts toward stage completion; 'observer' gets a comment-only task.","default":"approver"},"required":{"type":"boolean","title":"Required","description":"If true, every user resolved by this rule must approve (overrides quorum).","default":false}},"type":"object","required":["rule_type","rule_value"],"title":"ApproverRuleIn"},"PolicyOut":{"properties":{"id":{"type":"string","title":"Id"},"policy_key":{"type":"string","title":"Policy Key"},"version":{"type":"integer","title":"Version"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"status":{"type":"string","title":"Status"},"artifact_type":{"type":"string","title":"Artifact Type"},"forbid_self_approval":{"type":"boolean","title":"Forbid Self Approval","default":false},"forbid_repeat_approvers":{"type":"boolean","title":"Forbid Repeat Approvers","default":false},"created_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"stages":{"items":{"$ref":"#/components/schemas/StageOut"},"type":"array","title":"Stages"}},"type":"object","required":["id","policy_key","version","name","status","artifact_type","created_at","updated_at"],"title":"PolicyOut"},"StageOut":{"properties":{"name":{"type":"string","title":"Name"},"stage_order":{"type":"integer","minimum":1,"title":"Stage Order"},"mode":{"type":"string","title":"Mode","default":"all"},"mode_value":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"title":"Mode Value"},"sla_hours":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"title":"Sla Hours"},"skip_if":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Skip If"},"on_empty":{"type":"string","title":"On Empty","default":"block"},"parallel_group":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Parallel Group","description":"Stages sharing a parallel_group run concurrently and must all approve before the group completes. Null = stage is its own group (strictly sequential)."},"on_breach":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"On Breach","description":"What to do when every open task in the stage has crossed its due_at: 'notify' (default behaviour) / 'auto_approve' / 'auto_reject' / 'escalate'."},"escalation_rules":{"items":{"$ref":"#/components/schemas/ApproverRuleOut"},"type":"array","title":"Escalation Rules"},"rules":{"items":{"$ref":"#/components/schemas/ApproverRuleOut"},"type":"array","title":"Rules"},"id":{"type":"string","title":"Id"}},"type":"object","required":["name","stage_order","id"],"title":"StageOut"},"ApproverRuleOut":{"properties":{"rule_type":{"type":"string","title":"Rule Type"},"rule_value":{"additionalProperties":true,"type":"object","title":"Rule Value"},"kind":{"type":"string","title":"Kind","description":"'approver' counts toward stage completion; 'observer' gets a comment-only task.","default":"approver"},"required":{"type":"boolean","title":"Required","description":"If true, every user resolved by this rule must approve (overrides quorum).","default":false},"id":{"type":"string","title":"Id"}},"type":"object","required":["rule_type","rule_value","id"],"title":"ApproverRuleOut"},"ErrorResponse":{"properties":{"id":{"type":"string","title":"Id","default":"openg2p.awe"},"version":{"type":"string","title":"Version","default":"1.0"},"responsetime":{"type":"string","title":"Responsetime"},"response":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Response"},"errors":{"items":{"$ref":"#/components/schemas/ErrorDetail"},"type":"array","title":"Errors"}},"type":"object","required":["responsetime","errors"],"title":"ErrorResponse"},"ErrorDetail":{"properties":{"errorCode":{"type":"string","title":"Errorcode","description":"OpenG2P-assigned error code. AWE catalog: `AWE-001` (policy not found), `AWE-002` (policy conflict / version clash), `AWE-003` (request not found), `AWE-004` (task not found), `AWE-005` (service not ready — startup incomplete), `AWE-006` (database health check failed), `AWE-007` (invalid state transition), `AWE-008` (unauthorized / forbidden), `AWE-009` (idempotency key conflict with different payload), `AWE-010` (validation — bad policy definition)."},"message":{"type":"string","title":"Message"}},"type":"object","required":["errorCode","message"],"title":"ErrorDetail"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}},"paths":{"/v1/awe/policies/{policy_key}":{"put":{"tags":["policies"],"summary":"Add a new draft version under an existing policy_key","operationId":"add_version_v1_awe_policies__policy_key__put","parameters":[{"name":"policy_key","in":"path","required":true,"schema":{"type":"string","title":"Policy Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PolicyCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PolicyOut"}}}},"400":{"description":"Policy body violates a structural rule (e.g. URL/body key mismatch).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Bearer token missing, malformed, or fails signature/expiry checks.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Token is valid but lacks the `AWE_ADMIN` role required for this mutation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No matching policy / policy version exists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}}}
```

## GET /v1/awe/requests

> Search requests by artifact reference and/or status

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"tags":[{"name":"requests","description":"Service-to-service runtime endpoints. Callers POST here when an artifact (CR, disbursement, …) is created, cancel when the underlying artifact is withdrawn, and read the audit timeline for display."}],"security":[{"HTTPBearer":[]}],"components":{"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"}},"schemas":{"RequestOut":{"properties":{"id":{"type":"string","title":"Id"},"policy_id":{"type":"string","title":"Policy Id"},"policy_key":{"type":"string","title":"Policy Key"},"policy_version":{"type":"integer","title":"Policy Version"},"artifact_type":{"type":"string","title":"Artifact Type"},"artifact_id":{"type":"string","title":"Artifact Id"},"source_service":{"type":"string","title":"Source Service"},"requester":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Requester"},"context":{"additionalProperties":true,"type":"object","title":"Context"},"status":{"type":"string","title":"Status"},"current_stage_order":{"type":"integer","title":"Current Stage Order"},"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","policy_id","policy_key","policy_version","artifact_type","artifact_id","source_service","context","status","current_stage_order","created_at","updated_at"],"title":"RequestOut"},"ErrorResponse":{"properties":{"id":{"type":"string","title":"Id","default":"openg2p.awe"},"version":{"type":"string","title":"Version","default":"1.0"},"responsetime":{"type":"string","title":"Responsetime"},"response":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Response"},"errors":{"items":{"$ref":"#/components/schemas/ErrorDetail"},"type":"array","title":"Errors"}},"type":"object","required":["responsetime","errors"],"title":"ErrorResponse"},"ErrorDetail":{"properties":{"errorCode":{"type":"string","title":"Errorcode","description":"OpenG2P-assigned error code. AWE catalog: `AWE-001` (policy not found), `AWE-002` (policy conflict / version clash), `AWE-003` (request not found), `AWE-004` (task not found), `AWE-005` (service not ready — startup incomplete), `AWE-006` (database health check failed), `AWE-007` (invalid state transition), `AWE-008` (unauthorized / forbidden), `AWE-009` (idempotency key conflict with different payload), `AWE-010` (validation — bad policy definition)."},"message":{"type":"string","title":"Message"}},"type":"object","required":["errorCode","message"],"title":"ErrorDetail"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}},"paths":{"/v1/awe/requests":{"get":{"tags":["requests"],"summary":"Search requests by artifact reference and/or status","operationId":"search_requests_v1_awe_requests_get","parameters":[{"name":"artifact_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Artifact Type"}},{"name":"artifact_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Artifact Id"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RequestOut"},"title":"Response Search Requests V1 Awe Requests Get"}}}},"401":{"description":"Bearer token missing, malformed, or fails signature/expiry checks.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}}}
```

## POST /v1/awe/policies/{policy\_key}/versions/{version}/simulate

> Resolve approvers for a sample context — no DB writes

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"tags":[{"name":"policies","description":"Policy CRUD, versioning, activation, and simulation. **Admin surface only** — used by the bundled admin SPA and GitOps tooling. Caller services should never call these endpoints."}],"security":[{"HTTPBearer":[]}],"components":{"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"}},"schemas":{"SimulateRequest":{"properties":{"context":{"additionalProperties":true,"type":"object","title":"Context"}},"type":"object","title":"SimulateRequest"},"SimulateResponse":{"properties":{"policy_id":{"type":"string","title":"Policy Id"},"policy_version":{"type":"integer","title":"Policy Version"},"stages":{"items":{"$ref":"#/components/schemas/SimulateStageOut"},"type":"array","title":"Stages"}},"type":"object","required":["policy_id","policy_version","stages"],"title":"SimulateResponse"},"SimulateStageOut":{"properties":{"stage_order":{"type":"integer","title":"Stage Order"},"name":{"type":"string","title":"Name"},"mode":{"type":"string","title":"Mode"},"mode_value":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Mode Value"},"resolved_approvers":{"items":{"type":"string"},"type":"array","title":"Resolved Approvers"},"skipped":{"type":"boolean","title":"Skipped","default":false},"skip_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Skip Reason"}},"type":"object","required":["stage_order","name","mode","resolved_approvers"],"title":"SimulateStageOut"},"ErrorResponse":{"properties":{"id":{"type":"string","title":"Id","default":"openg2p.awe"},"version":{"type":"string","title":"Version","default":"1.0"},"responsetime":{"type":"string","title":"Responsetime"},"response":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Response"},"errors":{"items":{"$ref":"#/components/schemas/ErrorDetail"},"type":"array","title":"Errors"}},"type":"object","required":["responsetime","errors"],"title":"ErrorResponse"},"ErrorDetail":{"properties":{"errorCode":{"type":"string","title":"Errorcode","description":"OpenG2P-assigned error code. AWE catalog: `AWE-001` (policy not found), `AWE-002` (policy conflict / version clash), `AWE-003` (request not found), `AWE-004` (task not found), `AWE-005` (service not ready — startup incomplete), `AWE-006` (database health check failed), `AWE-007` (invalid state transition), `AWE-008` (unauthorized / forbidden), `AWE-009` (idempotency key conflict with different payload), `AWE-010` (validation — bad policy definition)."},"message":{"type":"string","title":"Message"}},"type":"object","required":["errorCode","message"],"title":"ErrorDetail"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}},"paths":{"/v1/awe/policies/{policy_key}/versions/{version}/simulate":{"post":{"tags":["policies"],"summary":"Resolve approvers for a sample context — no DB writes","operationId":"simulate_v1_awe_policies__policy_key__versions__version__simulate_post","parameters":[{"name":"policy_key","in":"path","required":true,"schema":{"type":"string","title":"Policy Key"}},{"name":"version","in":"path","required":true,"schema":{"type":"integer","title":"Version"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimulateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimulateResponse"}}}},"401":{"description":"Bearer token missing, malformed, or fails signature/expiry checks.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Token is valid but lacks `AWE_VIEWER` or `AWE_ADMIN` role.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No matching policy / policy version exists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"503":{"description":"An approver-resolution rule's upstream call failed (Keycloak admin API, HTTP resolver, etc.).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}}}
```

## POST /v1/awe/requests

> Create an approval request for a caller-owned artifact

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"tags":[{"name":"requests","description":"Service-to-service runtime endpoints. Callers POST here when an artifact (CR, disbursement, …) is created, cancel when the underlying artifact is withdrawn, and read the audit timeline for display."}],"security":[{"HTTPBearer":[]}],"components":{"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"}},"schemas":{"CreateRequestIn":{"properties":{"policy_key":{"type":"string","title":"Policy Key"},"artifact_type":{"type":"string","title":"Artifact Type"},"artifact_id":{"type":"string","title":"Artifact Id"},"context":{"additionalProperties":true,"type":"object","title":"Context"},"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url"},"callback_secret_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Secret Id"},"requester":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Requester"}},"type":"object","required":["policy_key","artifact_type","artifact_id"],"title":"CreateRequestIn"},"CreateRequestOut":{"properties":{"request_id":{"type":"string","title":"Request Id"},"status":{"type":"string","title":"Status"},"current_stage_order":{"type":"integer","title":"Current Stage Order"},"tasks":{"items":{"$ref":"#/components/schemas/TaskOut"},"type":"array","title":"Tasks"}},"type":"object","required":["request_id","status","current_stage_order"],"title":"CreateRequestOut"},"TaskOut":{"properties":{"id":{"type":"string","title":"Id"},"request_id":{"type":"string","title":"Request Id"},"stage_id":{"type":"string","title":"Stage Id"},"stage_order":{"type":"integer","title":"Stage Order"},"assignee":{"type":"string","title":"Assignee"},"kind":{"type":"string","title":"Kind","default":"approver"},"delegated_from":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Delegated From"},"reassigned_from":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reassigned From"},"status":{"type":"string","title":"Status"},"claimed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Claimed At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"due_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Due At"},"decision_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Decision Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","request_id","stage_id","stage_order","assignee","status","created_at"],"title":"TaskOut"},"ErrorResponse":{"properties":{"id":{"type":"string","title":"Id","default":"openg2p.awe"},"version":{"type":"string","title":"Version","default":"1.0"},"responsetime":{"type":"string","title":"Responsetime"},"response":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Response"},"errors":{"items":{"$ref":"#/components/schemas/ErrorDetail"},"type":"array","title":"Errors"}},"type":"object","required":["responsetime","errors"],"title":"ErrorResponse"},"ErrorDetail":{"properties":{"errorCode":{"type":"string","title":"Errorcode","description":"OpenG2P-assigned error code. AWE catalog: `AWE-001` (policy not found), `AWE-002` (policy conflict / version clash), `AWE-003` (request not found), `AWE-004` (task not found), `AWE-005` (service not ready — startup incomplete), `AWE-006` (database health check failed), `AWE-007` (invalid state transition), `AWE-008` (unauthorized / forbidden), `AWE-009` (idempotency key conflict with different payload), `AWE-010` (validation — bad policy definition)."},"message":{"type":"string","title":"Message"}},"type":"object","required":["errorCode","message"],"title":"ErrorDetail"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}},"paths":{"/v1/awe/requests":{"post":{"tags":["requests"],"summary":"Create an approval request for a caller-owned artifact","operationId":"create_request_v1_awe_requests_post","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idempotency-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRequestIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRequestOut"}}}},"400":{"description":"Engine refused to start the request (e.g. malformed context).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Bearer token missing, malformed, or fails signature/expiry checks.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Policy key has no version in `active` status — no flow can be started for it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}}}
```

## GET /v1/awe/requests/{request\_id}

> Fetch an approval request by id

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"tags":[{"name":"requests","description":"Service-to-service runtime endpoints. Callers POST here when an artifact (CR, disbursement, …) is created, cancel when the underlying artifact is withdrawn, and read the audit timeline for display."}],"security":[{"HTTPBearer":[]}],"components":{"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"}},"schemas":{"RequestOut":{"properties":{"id":{"type":"string","title":"Id"},"policy_id":{"type":"string","title":"Policy Id"},"policy_key":{"type":"string","title":"Policy Key"},"policy_version":{"type":"integer","title":"Policy Version"},"artifact_type":{"type":"string","title":"Artifact Type"},"artifact_id":{"type":"string","title":"Artifact Id"},"source_service":{"type":"string","title":"Source Service"},"requester":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Requester"},"context":{"additionalProperties":true,"type":"object","title":"Context"},"status":{"type":"string","title":"Status"},"current_stage_order":{"type":"integer","title":"Current Stage Order"},"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","policy_id","policy_key","policy_version","artifact_type","artifact_id","source_service","context","status","current_stage_order","created_at","updated_at"],"title":"RequestOut"},"ErrorResponse":{"properties":{"id":{"type":"string","title":"Id","default":"openg2p.awe"},"version":{"type":"string","title":"Version","default":"1.0"},"responsetime":{"type":"string","title":"Responsetime"},"response":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Response"},"errors":{"items":{"$ref":"#/components/schemas/ErrorDetail"},"type":"array","title":"Errors"}},"type":"object","required":["responsetime","errors"],"title":"ErrorResponse"},"ErrorDetail":{"properties":{"errorCode":{"type":"string","title":"Errorcode","description":"OpenG2P-assigned error code. AWE catalog: `AWE-001` (policy not found), `AWE-002` (policy conflict / version clash), `AWE-003` (request not found), `AWE-004` (task not found), `AWE-005` (service not ready — startup incomplete), `AWE-006` (database health check failed), `AWE-007` (invalid state transition), `AWE-008` (unauthorized / forbidden), `AWE-009` (idempotency key conflict with different payload), `AWE-010` (validation — bad policy definition)."},"message":{"type":"string","title":"Message"}},"type":"object","required":["errorCode","message"],"title":"ErrorDetail"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}},"paths":{"/v1/awe/requests/{request_id}":{"get":{"tags":["requests"],"summary":"Fetch an approval request by id","operationId":"get_request_v1_awe_requests__request_id__get","parameters":[{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestOut"}}}},"401":{"description":"Bearer token missing, malformed, or fails signature/expiry checks.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No approval request with the given id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}}}
```

## POST /v1/awe/requests/{request\_id}/cancel

> Cancel an in-flight approval request (admin only)

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"tags":[{"name":"requests","description":"Service-to-service runtime endpoints. Callers POST here when an artifact (CR, disbursement, …) is created, cancel when the underlying artifact is withdrawn, and read the audit timeline for display."}],"security":[{"HTTPBearer":[]}],"components":{"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"}},"schemas":{"CancelRequest":{"properties":{"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"},"actor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor"}},"type":"object","title":"CancelRequest"},"RequestOut":{"properties":{"id":{"type":"string","title":"Id"},"policy_id":{"type":"string","title":"Policy Id"},"policy_key":{"type":"string","title":"Policy Key"},"policy_version":{"type":"integer","title":"Policy Version"},"artifact_type":{"type":"string","title":"Artifact Type"},"artifact_id":{"type":"string","title":"Artifact Id"},"source_service":{"type":"string","title":"Source Service"},"requester":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Requester"},"context":{"additionalProperties":true,"type":"object","title":"Context"},"status":{"type":"string","title":"Status"},"current_stage_order":{"type":"integer","title":"Current Stage Order"},"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","policy_id","policy_key","policy_version","artifact_type","artifact_id","source_service","context","status","current_stage_order","created_at","updated_at"],"title":"RequestOut"},"ErrorResponse":{"properties":{"id":{"type":"string","title":"Id","default":"openg2p.awe"},"version":{"type":"string","title":"Version","default":"1.0"},"responsetime":{"type":"string","title":"Responsetime"},"response":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Response"},"errors":{"items":{"$ref":"#/components/schemas/ErrorDetail"},"type":"array","title":"Errors"}},"type":"object","required":["responsetime","errors"],"title":"ErrorResponse"},"ErrorDetail":{"properties":{"errorCode":{"type":"string","title":"Errorcode","description":"OpenG2P-assigned error code. AWE catalog: `AWE-001` (policy not found), `AWE-002` (policy conflict / version clash), `AWE-003` (request not found), `AWE-004` (task not found), `AWE-005` (service not ready — startup incomplete), `AWE-006` (database health check failed), `AWE-007` (invalid state transition), `AWE-008` (unauthorized / forbidden), `AWE-009` (idempotency key conflict with different payload), `AWE-010` (validation — bad policy definition)."},"message":{"type":"string","title":"Message"}},"type":"object","required":["errorCode","message"],"title":"ErrorDetail"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}},"paths":{"/v1/awe/requests/{request_id}/cancel":{"post":{"tags":["requests"],"summary":"Cancel an in-flight approval request (admin only)","operationId":"cancel_request_v1_awe_requests__request_id__cancel_post","parameters":[{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestOut"}}}},"401":{"description":"Bearer token missing, malformed, or fails signature/expiry checks.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Token is valid but lacks the `AWE_ADMIN` role required for this mutation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No approval request with the given id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Resource is in a state that disallows this transition. Examples: request is already in a terminal state (`approved`/`rejected`/`cancelled`); task is not `open`/`claimed`; policy version is not `draft` for editing; delivery has already succeeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}}}
```

## GET /v1/awe/requests/{request\_id}/events

> Timeline of every event for a request (audit log)

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"tags":[{"name":"requests","description":"Service-to-service runtime endpoints. Callers POST here when an artifact (CR, disbursement, …) is created, cancel when the underlying artifact is withdrawn, and read the audit timeline for display."}],"security":[{"HTTPBearer":[]}],"components":{"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"}},"schemas":{"EventOut":{"properties":{"id":{"type":"string","title":"Id"},"request_id":{"type":"string","title":"Request Id"},"event_type":{"type":"string","title":"Event Type"},"payload":{"additionalProperties":true,"type":"object","title":"Payload"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","request_id","event_type","payload","created_at"],"title":"EventOut"},"ErrorResponse":{"properties":{"id":{"type":"string","title":"Id","default":"openg2p.awe"},"version":{"type":"string","title":"Version","default":"1.0"},"responsetime":{"type":"string","title":"Responsetime"},"response":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Response"},"errors":{"items":{"$ref":"#/components/schemas/ErrorDetail"},"type":"array","title":"Errors"}},"type":"object","required":["responsetime","errors"],"title":"ErrorResponse"},"ErrorDetail":{"properties":{"errorCode":{"type":"string","title":"Errorcode","description":"OpenG2P-assigned error code. AWE catalog: `AWE-001` (policy not found), `AWE-002` (policy conflict / version clash), `AWE-003` (request not found), `AWE-004` (task not found), `AWE-005` (service not ready — startup incomplete), `AWE-006` (database health check failed), `AWE-007` (invalid state transition), `AWE-008` (unauthorized / forbidden), `AWE-009` (idempotency key conflict with different payload), `AWE-010` (validation — bad policy definition)."},"message":{"type":"string","title":"Message"}},"type":"object","required":["errorCode","message"],"title":"ErrorDetail"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}},"paths":{"/v1/awe/requests/{request_id}/events":{"get":{"tags":["requests"],"summary":"Timeline of every event for a request (audit log)","operationId":"request_events_v1_awe_requests__request_id__events_get","parameters":[{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/EventOut"},"title":"Response Request Events V1 Awe Requests  Request Id  Events Get"}}}},"401":{"description":"Bearer token missing, malformed, or fails signature/expiry checks.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}}}
```

## GET /v1/awe/tasks

> List tasks — by assignee (default = me) and/or by request\_id

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"tags":[{"name":"tasks","description":"Approver-facing endpoints — list inbox, claim, submit a decision. The caller service proxies these on behalf of the end-user approver; approvers never talk to AWE directly."}],"security":[{"HTTPBearer":[]}],"components":{"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"}},"schemas":{"TaskOut":{"properties":{"id":{"type":"string","title":"Id"},"request_id":{"type":"string","title":"Request Id"},"stage_id":{"type":"string","title":"Stage Id"},"stage_order":{"type":"integer","title":"Stage Order"},"assignee":{"type":"string","title":"Assignee"},"kind":{"type":"string","title":"Kind","default":"approver"},"delegated_from":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Delegated From"},"reassigned_from":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reassigned From"},"status":{"type":"string","title":"Status"},"claimed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Claimed At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"due_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Due At"},"decision_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Decision Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","request_id","stage_id","stage_order","assignee","status","created_at"],"title":"TaskOut"},"ErrorResponse":{"properties":{"id":{"type":"string","title":"Id","default":"openg2p.awe"},"version":{"type":"string","title":"Version","default":"1.0"},"responsetime":{"type":"string","title":"Responsetime"},"response":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Response"},"errors":{"items":{"$ref":"#/components/schemas/ErrorDetail"},"type":"array","title":"Errors"}},"type":"object","required":["responsetime","errors"],"title":"ErrorResponse"},"ErrorDetail":{"properties":{"errorCode":{"type":"string","title":"Errorcode","description":"OpenG2P-assigned error code. AWE catalog: `AWE-001` (policy not found), `AWE-002` (policy conflict / version clash), `AWE-003` (request not found), `AWE-004` (task not found), `AWE-005` (service not ready — startup incomplete), `AWE-006` (database health check failed), `AWE-007` (invalid state transition), `AWE-008` (unauthorized / forbidden), `AWE-009` (idempotency key conflict with different payload), `AWE-010` (validation — bad policy definition)."},"message":{"type":"string","title":"Message"}},"type":"object","required":["errorCode","message"],"title":"ErrorDetail"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}},"paths":{"/v1/awe/tasks":{"get":{"tags":["tasks"],"summary":"List tasks — by assignee (default = me) and/or by request_id","operationId":"list_tasks_v1_awe_tasks_get","parameters":[{"name":"assignee","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by assignee. Default `me` resolves to the token's `sub`. Pass `*` (or any non-`me` value) plus `request_id` to enumerate all tasks for a given request — used by the admin Request Detail page.","default":"me","title":"Assignee"},"description":"Filter by assignee. Default `me` resolves to the token's `sub`. Pass `*` (or any non-`me` value) plus `request_id` to enumerate all tasks for a given request — used by the admin Request Detail page."},{"name":"request_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Request Id"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":100,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TaskOut"},"title":"Response List Tasks V1 Awe Tasks Get"}}}},"401":{"description":"Bearer token missing, malformed, or fails signature/expiry checks.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}}}
```

## POST /v1/awe/tasks/{task\_id}/claim

> Claim a task (intent-to-act marker; not required for decision)

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"tags":[{"name":"tasks","description":"Approver-facing endpoints — list inbox, claim, submit a decision. The caller service proxies these on behalf of the end-user approver; approvers never talk to AWE directly."}],"security":[{"HTTPBearer":[]}],"components":{"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"}},"schemas":{"TaskOut":{"properties":{"id":{"type":"string","title":"Id"},"request_id":{"type":"string","title":"Request Id"},"stage_id":{"type":"string","title":"Stage Id"},"stage_order":{"type":"integer","title":"Stage Order"},"assignee":{"type":"string","title":"Assignee"},"kind":{"type":"string","title":"Kind","default":"approver"},"delegated_from":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Delegated From"},"reassigned_from":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reassigned From"},"status":{"type":"string","title":"Status"},"claimed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Claimed At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"due_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Due At"},"decision_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Decision Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","request_id","stage_id","stage_order","assignee","status","created_at"],"title":"TaskOut"},"ErrorResponse":{"properties":{"id":{"type":"string","title":"Id","default":"openg2p.awe"},"version":{"type":"string","title":"Version","default":"1.0"},"responsetime":{"type":"string","title":"Responsetime"},"response":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Response"},"errors":{"items":{"$ref":"#/components/schemas/ErrorDetail"},"type":"array","title":"Errors"}},"type":"object","required":["responsetime","errors"],"title":"ErrorResponse"},"ErrorDetail":{"properties":{"errorCode":{"type":"string","title":"Errorcode","description":"OpenG2P-assigned error code. AWE catalog: `AWE-001` (policy not found), `AWE-002` (policy conflict / version clash), `AWE-003` (request not found), `AWE-004` (task not found), `AWE-005` (service not ready — startup incomplete), `AWE-006` (database health check failed), `AWE-007` (invalid state transition), `AWE-008` (unauthorized / forbidden), `AWE-009` (idempotency key conflict with different payload), `AWE-010` (validation — bad policy definition)."},"message":{"type":"string","title":"Message"}},"type":"object","required":["errorCode","message"],"title":"ErrorDetail"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}},"paths":{"/v1/awe/tasks/{task_id}/claim":{"post":{"tags":["tasks"],"summary":"Claim a task (intent-to-act marker; not required for decision)","operationId":"claim_task_v1_awe_tasks__task_id__claim_post","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskOut"}}}},"401":{"description":"Bearer token missing, malformed, or fails signature/expiry checks.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Task is not assigned to the caller and the caller is not an admin.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No task with the given id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Resource is in a state that disallows this transition. Examples: request is already in a terminal state (`approved`/`rejected`/`cancelled`); task is not `open`/`claimed`; policy version is not `draft` for editing; delivery has already succeeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}}}
```

## POST /v1/awe/tasks/{task\_id}/decision

> Record a decision (approve / reject / abstain) on a task

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"tags":[{"name":"tasks","description":"Approver-facing endpoints — list inbox, claim, submit a decision. The caller service proxies these on behalf of the end-user approver; approvers never talk to AWE directly."}],"security":[{"HTTPBearer":[]}],"components":{"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"}},"schemas":{"DecisionIn":{"properties":{"action":{"type":"string","title":"Action"},"comment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Comment"},"attachments_ref":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Attachments Ref"}},"type":"object","required":["action"],"title":"DecisionIn"},"DecisionOut":{"properties":{"id":{"type":"string","title":"Id"},"request_id":{"type":"string","title":"Request Id"},"task_id":{"type":"string","title":"Task Id"},"stage_order":{"type":"integer","title":"Stage Order"},"actor":{"type":"string","title":"Actor"},"action":{"type":"string","title":"Action"},"comment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Comment"},"attachments_ref":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Attachments Ref"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","request_id","task_id","stage_order","actor","action","created_at"],"title":"DecisionOut"},"ErrorResponse":{"properties":{"id":{"type":"string","title":"Id","default":"openg2p.awe"},"version":{"type":"string","title":"Version","default":"1.0"},"responsetime":{"type":"string","title":"Responsetime"},"response":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Response"},"errors":{"items":{"$ref":"#/components/schemas/ErrorDetail"},"type":"array","title":"Errors"}},"type":"object","required":["responsetime","errors"],"title":"ErrorResponse"},"ErrorDetail":{"properties":{"errorCode":{"type":"string","title":"Errorcode","description":"OpenG2P-assigned error code. AWE catalog: `AWE-001` (policy not found), `AWE-002` (policy conflict / version clash), `AWE-003` (request not found), `AWE-004` (task not found), `AWE-005` (service not ready — startup incomplete), `AWE-006` (database health check failed), `AWE-007` (invalid state transition), `AWE-008` (unauthorized / forbidden), `AWE-009` (idempotency key conflict with different payload), `AWE-010` (validation — bad policy definition)."},"message":{"type":"string","title":"Message"}},"type":"object","required":["errorCode","message"],"title":"ErrorDetail"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}},"paths":{"/v1/awe/tasks/{task_id}/decision":{"post":{"tags":["tasks"],"summary":"Record a decision (approve / reject / abstain) on a task","operationId":"decide_v1_awe_tasks__task_id__decision_post","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecisionIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecisionOut"}}}},"401":{"description":"Bearer token missing, malformed, or fails signature/expiry checks.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Task is not assigned to the caller and the caller is not an admin.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No approval request with the given id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Resource is in a state that disallows this transition. Examples: request is already in a terminal state (`approved`/`rejected`/`cancelled`); task is not `open`/`claimed`; policy version is not `draft` for editing; delivery has already succeeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}}}
```

## Webhook (outbound from AWE → Caller)

AWE POSTs to whatever `callback_url` was set on the request whenever a status-changing event occurs. The contract — body schema and the three signed headers — is declared in the OpenAPI spec under the top-level `webhooks:` field (OpenAPI 3.1 feature) so it's discoverable from the same artifact as the rest of the API.

{% openapi-webhook spec="awe-specification" name="approval-event" method="post" %}
[awe-specification](https://raw.githubusercontent.com/OpenG2P/awe/develop/docs/openapi.json)
{% endopenapi-webhook %}

## The ApproverRuleIn object

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"components":{"schemas":{"ApproverRuleIn":{"properties":{"rule_type":{"type":"string","title":"Rule Type"},"rule_value":{"additionalProperties":true,"type":"object","title":"Rule Value"},"kind":{"type":"string","title":"Kind","description":"'approver' counts toward stage completion; 'observer' gets a comment-only task.","default":"approver"},"required":{"type":"boolean","title":"Required","description":"If true, every user resolved by this rule must approve (overrides quorum).","default":false}},"type":"object","required":["rule_type","rule_value"],"title":"ApproverRuleIn"}}}}
```

## The ApproverRuleOut object

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"components":{"schemas":{"ApproverRuleOut":{"properties":{"rule_type":{"type":"string","title":"Rule Type"},"rule_value":{"additionalProperties":true,"type":"object","title":"Rule Value"},"kind":{"type":"string","title":"Kind","description":"'approver' counts toward stage completion; 'observer' gets a comment-only task.","default":"approver"},"required":{"type":"boolean","title":"Required","description":"If true, every user resolved by this rule must approve (overrides quorum).","default":false},"id":{"type":"string","title":"Id"}},"type":"object","required":["rule_type","rule_value","id"],"title":"ApproverRuleOut"}}}}
```

## The CancelRequest object

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"components":{"schemas":{"CancelRequest":{"properties":{"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"},"actor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor"}},"type":"object","title":"CancelRequest"}}}}
```

## The CreateRequestIn object

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"components":{"schemas":{"CreateRequestIn":{"properties":{"policy_key":{"type":"string","title":"Policy Key"},"artifact_type":{"type":"string","title":"Artifact Type"},"artifact_id":{"type":"string","title":"Artifact Id"},"context":{"additionalProperties":true,"type":"object","title":"Context"},"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url"},"callback_secret_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Secret Id"},"requester":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Requester"}},"type":"object","required":["policy_key","artifact_type","artifact_id"],"title":"CreateRequestIn"}}}}
```

## The CreateRequestOut object

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"components":{"schemas":{"CreateRequestOut":{"properties":{"request_id":{"type":"string","title":"Request Id"},"status":{"type":"string","title":"Status"},"current_stage_order":{"type":"integer","title":"Current Stage Order"},"tasks":{"items":{"$ref":"#/components/schemas/TaskOut"},"type":"array","title":"Tasks"}},"type":"object","required":["request_id","status","current_stage_order"],"title":"CreateRequestOut"},"TaskOut":{"properties":{"id":{"type":"string","title":"Id"},"request_id":{"type":"string","title":"Request Id"},"stage_id":{"type":"string","title":"Stage Id"},"stage_order":{"type":"integer","title":"Stage Order"},"assignee":{"type":"string","title":"Assignee"},"kind":{"type":"string","title":"Kind","default":"approver"},"delegated_from":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Delegated From"},"reassigned_from":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reassigned From"},"status":{"type":"string","title":"Status"},"claimed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Claimed At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"due_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Due At"},"decision_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Decision Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","request_id","stage_id","stage_order","assignee","status","created_at"],"title":"TaskOut"}}}}
```

## The DecisionIn object

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"components":{"schemas":{"DecisionIn":{"properties":{"action":{"type":"string","title":"Action"},"comment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Comment"},"attachments_ref":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Attachments Ref"}},"type":"object","required":["action"],"title":"DecisionIn"}}}}
```

## The DecisionOut object

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"components":{"schemas":{"DecisionOut":{"properties":{"id":{"type":"string","title":"Id"},"request_id":{"type":"string","title":"Request Id"},"task_id":{"type":"string","title":"Task Id"},"stage_order":{"type":"integer","title":"Stage Order"},"actor":{"type":"string","title":"Actor"},"action":{"type":"string","title":"Action"},"comment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Comment"},"attachments_ref":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Attachments Ref"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","request_id","task_id","stage_order","actor","action","created_at"],"title":"DecisionOut"}}}}
```

## The ErrorDetail object

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"components":{"schemas":{"ErrorDetail":{"properties":{"errorCode":{"type":"string","title":"Errorcode","description":"OpenG2P-assigned error code. AWE catalog: `AWE-001` (policy not found), `AWE-002` (policy conflict / version clash), `AWE-003` (request not found), `AWE-004` (task not found), `AWE-005` (service not ready — startup incomplete), `AWE-006` (database health check failed), `AWE-007` (invalid state transition), `AWE-008` (unauthorized / forbidden), `AWE-009` (idempotency key conflict with different payload), `AWE-010` (validation — bad policy definition)."},"message":{"type":"string","title":"Message"}},"type":"object","required":["errorCode","message"],"title":"ErrorDetail"}}}}
```

## The EventOut object

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"components":{"schemas":{"EventOut":{"properties":{"id":{"type":"string","title":"Id"},"request_id":{"type":"string","title":"Request Id"},"event_type":{"type":"string","title":"Event Type"},"payload":{"additionalProperties":true,"type":"object","title":"Payload"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","request_id","event_type","payload","created_at"],"title":"EventOut"}}}}
```

## The HTTPValidationError object

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"components":{"schemas":{"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}}}
```

## The HealthPayload object

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"components":{"schemas":{"HealthPayload":{"properties":{"status":{"type":"string","title":"Status"}},"type":"object","required":["status"],"title":"HealthPayload"}}}}
```

## The HealthResponse object

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"components":{"schemas":{"HealthResponse":{"properties":{"id":{"type":"string","title":"Id","default":"openg2p.awe"},"version":{"type":"string","title":"Version","default":"1.0"},"responsetime":{"type":"string","title":"Responsetime"},"response":{"$ref":"#/components/schemas/HealthPayload"},"errors":{"items":{"$ref":"#/components/schemas/ErrorDetail"},"type":"array","title":"Errors"}},"type":"object","required":["responsetime","response"],"title":"HealthResponse"},"HealthPayload":{"properties":{"status":{"type":"string","title":"Status"}},"type":"object","required":["status"],"title":"HealthPayload"},"ErrorDetail":{"properties":{"errorCode":{"type":"string","title":"Errorcode","description":"OpenG2P-assigned error code. AWE catalog: `AWE-001` (policy not found), `AWE-002` (policy conflict / version clash), `AWE-003` (request not found), `AWE-004` (task not found), `AWE-005` (service not ready — startup incomplete), `AWE-006` (database health check failed), `AWE-007` (invalid state transition), `AWE-008` (unauthorized / forbidden), `AWE-009` (idempotency key conflict with different payload), `AWE-010` (validation — bad policy definition)."},"message":{"type":"string","title":"Message"}},"type":"object","required":["errorCode","message"],"title":"ErrorDetail"}}}}
```

## The PolicyCreate object

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"components":{"schemas":{"PolicyCreate":{"properties":{"policy_key":{"type":"string","title":"Policy Key"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"artifact_type":{"type":"string","title":"Artifact Type"},"forbid_self_approval":{"type":"boolean","title":"Forbid Self Approval","description":"Filter the request's requester out of every stage's approver list.","default":false},"forbid_repeat_approvers":{"type":"boolean","title":"Forbid Repeat Approvers","description":"Filter users who approved an earlier stage out of later stages.","default":false},"stages":{"items":{"$ref":"#/components/schemas/StageIn"},"type":"array","title":"Stages"}},"type":"object","required":["policy_key","name","artifact_type"],"title":"PolicyCreate"},"StageIn":{"properties":{"name":{"type":"string","title":"Name"},"stage_order":{"type":"integer","minimum":1,"title":"Stage Order"},"mode":{"type":"string","title":"Mode","default":"all"},"mode_value":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"title":"Mode Value"},"sla_hours":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"title":"Sla Hours"},"skip_if":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Skip If"},"on_empty":{"type":"string","title":"On Empty","default":"block"},"parallel_group":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Parallel Group","description":"Stages sharing a parallel_group run concurrently and must all approve before the group completes. Null = stage is its own group (strictly sequential)."},"on_breach":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"On Breach","description":"What to do when every open task in the stage has crossed its due_at: 'notify' (default behaviour) / 'auto_approve' / 'auto_reject' / 'escalate'."},"escalation_rules":{"items":{"$ref":"#/components/schemas/ApproverRuleIn"},"type":"array","title":"Escalation Rules","description":"Rules resolved to add fresh approvers when on_breach='escalate'."},"rules":{"items":{"$ref":"#/components/schemas/ApproverRuleIn"},"type":"array","title":"Rules"}},"type":"object","required":["name","stage_order"],"title":"StageIn"},"ApproverRuleIn":{"properties":{"rule_type":{"type":"string","title":"Rule Type"},"rule_value":{"additionalProperties":true,"type":"object","title":"Rule Value"},"kind":{"type":"string","title":"Kind","description":"'approver' counts toward stage completion; 'observer' gets a comment-only task.","default":"approver"},"required":{"type":"boolean","title":"Required","description":"If true, every user resolved by this rule must approve (overrides quorum).","default":false}},"type":"object","required":["rule_type","rule_value"],"title":"ApproverRuleIn"}}}}
```

## The PolicyOut object

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"components":{"schemas":{"PolicyOut":{"properties":{"id":{"type":"string","title":"Id"},"policy_key":{"type":"string","title":"Policy Key"},"version":{"type":"integer","title":"Version"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"status":{"type":"string","title":"Status"},"artifact_type":{"type":"string","title":"Artifact Type"},"forbid_self_approval":{"type":"boolean","title":"Forbid Self Approval","default":false},"forbid_repeat_approvers":{"type":"boolean","title":"Forbid Repeat Approvers","default":false},"created_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"stages":{"items":{"$ref":"#/components/schemas/StageOut"},"type":"array","title":"Stages"}},"type":"object","required":["id","policy_key","version","name","status","artifact_type","created_at","updated_at"],"title":"PolicyOut"},"StageOut":{"properties":{"name":{"type":"string","title":"Name"},"stage_order":{"type":"integer","minimum":1,"title":"Stage Order"},"mode":{"type":"string","title":"Mode","default":"all"},"mode_value":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"title":"Mode Value"},"sla_hours":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"title":"Sla Hours"},"skip_if":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Skip If"},"on_empty":{"type":"string","title":"On Empty","default":"block"},"parallel_group":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Parallel Group","description":"Stages sharing a parallel_group run concurrently and must all approve before the group completes. Null = stage is its own group (strictly sequential)."},"on_breach":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"On Breach","description":"What to do when every open task in the stage has crossed its due_at: 'notify' (default behaviour) / 'auto_approve' / 'auto_reject' / 'escalate'."},"escalation_rules":{"items":{"$ref":"#/components/schemas/ApproverRuleOut"},"type":"array","title":"Escalation Rules"},"rules":{"items":{"$ref":"#/components/schemas/ApproverRuleOut"},"type":"array","title":"Rules"},"id":{"type":"string","title":"Id"}},"type":"object","required":["name","stage_order","id"],"title":"StageOut"},"ApproverRuleOut":{"properties":{"rule_type":{"type":"string","title":"Rule Type"},"rule_value":{"additionalProperties":true,"type":"object","title":"Rule Value"},"kind":{"type":"string","title":"Kind","description":"'approver' counts toward stage completion; 'observer' gets a comment-only task.","default":"approver"},"required":{"type":"boolean","title":"Required","description":"If true, every user resolved by this rule must approve (overrides quorum).","default":false},"id":{"type":"string","title":"Id"}},"type":"object","required":["rule_type","rule_value","id"],"title":"ApproverRuleOut"}}}}
```

## The PolicyVersionOut object

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"components":{"schemas":{"PolicyVersionOut":{"properties":{"id":{"type":"string","title":"Id"},"policy_key":{"type":"string","title":"Policy Key"},"version":{"type":"integer","title":"Version"},"status":{"type":"string","title":"Status"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","policy_key","version","status","created_at"],"title":"PolicyVersionOut"}}}}
```

## The RequestOut object

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"components":{"schemas":{"RequestOut":{"properties":{"id":{"type":"string","title":"Id"},"policy_id":{"type":"string","title":"Policy Id"},"policy_key":{"type":"string","title":"Policy Key"},"policy_version":{"type":"integer","title":"Policy Version"},"artifact_type":{"type":"string","title":"Artifact Type"},"artifact_id":{"type":"string","title":"Artifact Id"},"source_service":{"type":"string","title":"Source Service"},"requester":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Requester"},"context":{"additionalProperties":true,"type":"object","title":"Context"},"status":{"type":"string","title":"Status"},"current_stage_order":{"type":"integer","title":"Current Stage Order"},"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","policy_id","policy_key","policy_version","artifact_type","artifact_id","source_service","context","status","current_stage_order","created_at","updated_at"],"title":"RequestOut"}}}}
```

## The SimulateRequest object

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"components":{"schemas":{"SimulateRequest":{"properties":{"context":{"additionalProperties":true,"type":"object","title":"Context"}},"type":"object","title":"SimulateRequest"}}}}
```

## The SimulateResponse object

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"components":{"schemas":{"SimulateResponse":{"properties":{"policy_id":{"type":"string","title":"Policy Id"},"policy_version":{"type":"integer","title":"Policy Version"},"stages":{"items":{"$ref":"#/components/schemas/SimulateStageOut"},"type":"array","title":"Stages"}},"type":"object","required":["policy_id","policy_version","stages"],"title":"SimulateResponse"},"SimulateStageOut":{"properties":{"stage_order":{"type":"integer","title":"Stage Order"},"name":{"type":"string","title":"Name"},"mode":{"type":"string","title":"Mode"},"mode_value":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Mode Value"},"resolved_approvers":{"items":{"type":"string"},"type":"array","title":"Resolved Approvers"},"skipped":{"type":"boolean","title":"Skipped","default":false},"skip_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Skip Reason"}},"type":"object","required":["stage_order","name","mode","resolved_approvers"],"title":"SimulateStageOut"}}}}
```

## The SimulateStageOut object

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"components":{"schemas":{"SimulateStageOut":{"properties":{"stage_order":{"type":"integer","title":"Stage Order"},"name":{"type":"string","title":"Name"},"mode":{"type":"string","title":"Mode"},"mode_value":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Mode Value"},"resolved_approvers":{"items":{"type":"string"},"type":"array","title":"Resolved Approvers"},"skipped":{"type":"boolean","title":"Skipped","default":false},"skip_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Skip Reason"}},"type":"object","required":["stage_order","name","mode","resolved_approvers"],"title":"SimulateStageOut"}}}}
```

## The StageIn object

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"components":{"schemas":{"StageIn":{"properties":{"name":{"type":"string","title":"Name"},"stage_order":{"type":"integer","minimum":1,"title":"Stage Order"},"mode":{"type":"string","title":"Mode","default":"all"},"mode_value":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"title":"Mode Value"},"sla_hours":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"title":"Sla Hours"},"skip_if":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Skip If"},"on_empty":{"type":"string","title":"On Empty","default":"block"},"parallel_group":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Parallel Group","description":"Stages sharing a parallel_group run concurrently and must all approve before the group completes. Null = stage is its own group (strictly sequential)."},"on_breach":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"On Breach","description":"What to do when every open task in the stage has crossed its due_at: 'notify' (default behaviour) / 'auto_approve' / 'auto_reject' / 'escalate'."},"escalation_rules":{"items":{"$ref":"#/components/schemas/ApproverRuleIn"},"type":"array","title":"Escalation Rules","description":"Rules resolved to add fresh approvers when on_breach='escalate'."},"rules":{"items":{"$ref":"#/components/schemas/ApproverRuleIn"},"type":"array","title":"Rules"}},"type":"object","required":["name","stage_order"],"title":"StageIn"},"ApproverRuleIn":{"properties":{"rule_type":{"type":"string","title":"Rule Type"},"rule_value":{"additionalProperties":true,"type":"object","title":"Rule Value"},"kind":{"type":"string","title":"Kind","description":"'approver' counts toward stage completion; 'observer' gets a comment-only task.","default":"approver"},"required":{"type":"boolean","title":"Required","description":"If true, every user resolved by this rule must approve (overrides quorum).","default":false}},"type":"object","required":["rule_type","rule_value"],"title":"ApproverRuleIn"}}}}
```

## The StageOut object

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"components":{"schemas":{"StageOut":{"properties":{"name":{"type":"string","title":"Name"},"stage_order":{"type":"integer","minimum":1,"title":"Stage Order"},"mode":{"type":"string","title":"Mode","default":"all"},"mode_value":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"title":"Mode Value"},"sla_hours":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"title":"Sla Hours"},"skip_if":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Skip If"},"on_empty":{"type":"string","title":"On Empty","default":"block"},"parallel_group":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Parallel Group","description":"Stages sharing a parallel_group run concurrently and must all approve before the group completes. Null = stage is its own group (strictly sequential)."},"on_breach":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"On Breach","description":"What to do when every open task in the stage has crossed its due_at: 'notify' (default behaviour) / 'auto_approve' / 'auto_reject' / 'escalate'."},"escalation_rules":{"items":{"$ref":"#/components/schemas/ApproverRuleOut"},"type":"array","title":"Escalation Rules"},"rules":{"items":{"$ref":"#/components/schemas/ApproverRuleOut"},"type":"array","title":"Rules"},"id":{"type":"string","title":"Id"}},"type":"object","required":["name","stage_order","id"],"title":"StageOut"},"ApproverRuleOut":{"properties":{"rule_type":{"type":"string","title":"Rule Type"},"rule_value":{"additionalProperties":true,"type":"object","title":"Rule Value"},"kind":{"type":"string","title":"Kind","description":"'approver' counts toward stage completion; 'observer' gets a comment-only task.","default":"approver"},"required":{"type":"boolean","title":"Required","description":"If true, every user resolved by this rule must approve (overrides quorum).","default":false},"id":{"type":"string","title":"Id"}},"type":"object","required":["rule_type","rule_value","id"],"title":"ApproverRuleOut"}}}}
```

## The TaskOut object

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"components":{"schemas":{"TaskOut":{"properties":{"id":{"type":"string","title":"Id"},"request_id":{"type":"string","title":"Request Id"},"stage_id":{"type":"string","title":"Stage Id"},"stage_order":{"type":"integer","title":"Stage Order"},"assignee":{"type":"string","title":"Assignee"},"kind":{"type":"string","title":"Kind","default":"approver"},"delegated_from":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Delegated From"},"reassigned_from":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reassigned From"},"status":{"type":"string","title":"Status"},"claimed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Claimed At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"due_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Due At"},"decision_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Decision Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","request_id","stage_id","stage_order","assignee","status","created_at"],"title":"TaskOut"}}}}
```

## The ValidationError object

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"components":{"schemas":{"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}}}
```

## The VersionPayload object

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"components":{"schemas":{"VersionPayload":{"properties":{"service_version":{"type":"string","title":"Service Version"},"build_time":{"type":"string","title":"Build Time"},"git_commit":{"type":"string","title":"Git Commit"}},"type":"object","required":["service_version","build_time","git_commit"],"title":"VersionPayload"}}}}
```

## The VersionResponse object

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"components":{"schemas":{"VersionResponse":{"properties":{"id":{"type":"string","title":"Id","default":"openg2p.awe"},"version":{"type":"string","title":"Version","default":"1.0"},"responsetime":{"type":"string","title":"Responsetime"},"response":{"$ref":"#/components/schemas/VersionPayload"},"errors":{"items":{"$ref":"#/components/schemas/ErrorDetail"},"type":"array","title":"Errors"}},"type":"object","required":["responsetime","response"],"title":"VersionResponse"},"VersionPayload":{"properties":{"service_version":{"type":"string","title":"Service Version"},"build_time":{"type":"string","title":"Build Time"},"git_commit":{"type":"string","title":"Git Commit"}},"type":"object","required":["service_version","build_time","git_commit"],"title":"VersionPayload"},"ErrorDetail":{"properties":{"errorCode":{"type":"string","title":"Errorcode","description":"OpenG2P-assigned error code. AWE catalog: `AWE-001` (policy not found), `AWE-002` (policy conflict / version clash), `AWE-003` (request not found), `AWE-004` (task not found), `AWE-005` (service not ready — startup incomplete), `AWE-006` (database health check failed), `AWE-007` (invalid state transition), `AWE-008` (unauthorized / forbidden), `AWE-009` (idempotency key conflict with different payload), `AWE-010` (validation — bad policy definition)."},"message":{"type":"string","title":"Message"}},"type":"object","required":["errorCode","message"],"title":"ErrorDetail"}}}}
```

## The WebhookEvent object

```json
{"openapi":"3.1.0","info":{"title":"OpenG2P Approval Workflow Engine","version":"0.1.0"},"components":{"schemas":{"WebhookEvent":{"properties":{"event_id":{"type":"string","title":"Event Id"},"event_type":{"type":"string","title":"Event Type","description":"request_created | stage_started | stage_completed | request_approved | request_rejected | request_cancelled"},"request_id":{"type":"string","title":"Request Id"},"artifact_type":{"type":"string","title":"Artifact Type"},"artifact_id":{"type":"string","title":"Artifact Id"},"status":{"type":"string","title":"Status"},"stage_order":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Stage Order"},"actor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor"},"occurred_at":{"type":"string","format":"date-time","title":"Occurred At"}},"type":"object","required":["event_id","event_type","request_id","artifact_type","artifact_id","status","occurred_at"],"title":"WebhookEvent","description":"The body POSTed to the caller's `callback_url`.\n\nHeaders (set by the dispatcher, not part of this body):\n  X-Approval-Event-Id   — same as `event_id` below\n  X-Approval-Signature  — `sha256=<hex>`, HMAC over the raw body\n  X-Approval-Timestamp  — Unix seconds, included in the signed payload"}}}}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.openg2p.org/platform/platform-services/approval-workflow-engine/api-reference.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
