> ## Documentation Index
> Fetch the complete documentation index at: https://docs.awaithumans.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# POST /api/tasks/{id}/complete

> Submit a human's response. Triggers the verifier if configured.

```http theme={"system"}
POST /api/tasks/{id}/complete
Authorization: Bearer <ADMIN_API_TOKEN>   (or session cookie)
Content-Type: application/json
```

Records the human's response and transitions the task toward a terminal status. If a verifier is configured, this endpoint runs the LLM call inline before deciding the final status.

The dashboard's "Submit" button hits this endpoint. So do the Slack modal `view_submission` handler and the email magic-link confirmation page (each with their own auth shape — Slack signature, magic-link token).

## Authorization

| Caller               | Allowed? |
| -------------------- | -------- |
| Admin bearer token   | ✓        |
| Operator session     | ✓        |
| Assignee session     | ✓        |
| Other logged-in user | 403      |

The assignee check is the security-critical bit — a non-operator can complete only their own assigned tasks.

## Request

```json theme={"system"}
{
  "response": { "approved": true, "notes": "Duplicate charge confirmed." },
  "completed_by_email": null,
  "completed_via_channel": null
}
```

| Field                   | Type           | Required | Description                                                                                     |
| ----------------------- | -------------- | -------- | ----------------------------------------------------------------------------------------------- |
| `response`              | object         | yes      | Must validate against the task's `response_schema`.                                             |
| `completed_by_email`    | string \| null | no       | Override stamped attribution. Browser-driven calls leave null and the server reads the session. |
| `completed_via_channel` | string \| null | no       | Free-form channel identifier (`dashboard`, `slack`, `email`). Used by the audit log.            |

## Response

```http theme={"system"}
HTTP/1.1 200 OK
Content-Type: application/json
```

The full task record (same shape as `POST /api/tasks` response). The `status` field reflects what happened:

* `completed` — verifier passed (or no verifier). Final.
* `rejected` — verifier rejected this attempt; the task is non-terminal, the human can resubmit.
* `verification_exhausted` — verifier rejected, attempts exhausted. Final.

Inspect `verifier_result.reason` for the verifier's explanation when `rejected` / `verification_exhausted`.

## With verifier

If `task.verifier_config` is set:

1. Server runs the LLM call inline (5–30s typical).
2. Pass → `status=completed`, `response` saved, audit log records `action=verified`.
3. Fail with attempts left → `status=rejected`, `verification_attempt` bumped, audit log records `action=rejected` + `verifier_reason`. The task stays open for resubmission.
4. Fail with attempts exhausted → `status=verification_exhausted` (terminal), audit log records `action=verification_exhausted`.

Provider-level failures (vendor outage, missing API key, missing SDK extra) propagate as 5xx with typed `error_code` and DO NOT consume an attempt.

## With redact\_payload=True

The verifier is **skipped entirely**. The operator marked the payload sensitive; we don't ship it to a third-party LLM. Status goes straight to `completed`.

## Errors

| Status | `error_code`                    | Cause                                                              |
| ------ | ------------------------------- | ------------------------------------------------------------------ |
| 400    | (validation)                    | Body shape mismatch.                                               |
| 401    | —                               | Missing / invalid auth.                                            |
| 403    | —                               | Caller isn't operator / admin / assignee.                          |
| 404    | `TASK_NOT_FOUND`                | Task ID doesn't exist.                                             |
| 409    | `TASK_ALREADY_TERMINAL`         | Task is already in a terminal status (race with timeout / cancel). |
| 422    | `VERIFIER_CONFIG_INVALID`       | Stored verifier config doesn't match the current schema.           |
| 422    | `VERIFIER_PROVIDER_UNKNOWN`     | Operator typo'd the provider name.                                 |
| 500    | `VERIFIER_API_KEY_MISSING`      | Server can't read the LLM API key.                                 |
| 500    | `VERIFIER_PROVIDER_UNAVAILABLE` | SDK extra not installed.                                           |
| 502    | `VERIFIER_PROVIDER_ERROR`       | Vendor returned an error.                                          |

Each error body has the typed `error_code` + `docs` URL.

## Outbound webhook

If `task.callback_url` is set AND the task transitions to a terminal status, an HMAC-signed POST fires to the callback URL after the response is sent (FastAPI BackgroundTask — the human's submit doesn't wait on it). See [Self-hosting → Security](/self-hosting/security) for the signature scheme.

## SDK equivalent

The dashboard calls this directly from the form submission. From your own code:

```bash theme={"system"}
curl -X POST http://localhost:3001/api/tasks/tsk_abc/complete \
  -H "Authorization: Bearer $AWAITHUMANS_ADMIN_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"response": {"approved": true}}'
```

For the typical agent loop (create + poll), use the SDK's `await_human()`.
