---
slug: developer-api-create-read-tasks
title: Create and read Tasks
category: capability
status: published
tags: [create task, read task, poll, status, result, usage, 创建任务, 查询状态, 轮询]
aliases: []
lastEditAt: 2026-08-02
---
## Create a Task

```bash
curl -sS https://tycoon.us/api/public/tasks \
  -H "Authorization: Bearer $TYCOON_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: organic-growth-2026-08-01" \
  -d '{
    "prompt": "Improve organic growth through durable, evidence-backed work.",
    "title": "Organic-growth outcome",
    "workspace_id": "<bound-growth-workspace-id>",
    "budget_usd": 250,
    "working_guidance": "Prefer reversible SEO experiments and concise weekly updates.",
    "playbook_id": "<existing-company-skill-id>",
    "outcome": {
      "target": "Increase qualified organic clicks by 305% from the measured baseline.",
      "deadline_at": "2026-11-01T00:00:00.000Z",
      "metric_id": "<existing-workspace-metric-id>"
    }
  }'
```

First response (`201`):

```json
{
  "task": {
    "id": "cm...",
    "title": "Competitor research",
    "status": "queued",
    "created_at": "2026-08-01T12:00:00.000Z"
  },
  "workspace": {
    "id": "cm...",
    "name": "Growth",
    "slug": "growth",
    "task_bound": false
  },
  "idempotent_replay": false
}
```

`prompt` is required and limited to 20,000 characters. `title` (200
characters), `workspace_id`, and `budget_usd` (at most 100,000) are optional.
Omitting `workspace_id` creates an isolated task-bound workspace; specifying it
requires either the API key's controller workspace or a currently valid
explicit binding. Omitting `budget_usd` means no per-Task ceiling; it does not
make workspace usage free.

The response's `workspace` is the immutable execution home for that Task. A
later request may use a different or newly created workspace, but no API call
can migrate an existing Task. The key controller workspace remains the payer
for every one of those Tasks and their direct child contributions.

The optional durable-outcome fields — `outcome`, `working_guidance`, and
`playbook_id` — are described under *Long-running outcomes*.

`skill` is the more explicit Skill input. Send either `skill.id` (an existing
Company Skill in the execution workspace) or a Markdown-only `skill.upload`:

```json
"skill": {
  "upload": {
    "name": "SEO experiment method",
    "description": "Customer-supplied operating method for this one Task.",
    "markdown": "# Method\n1. Establish the baseline...",
    "tags": ["seo", "experiment"]
  }
}
```

Tycoon creates an Tycoon Agent-private Skill in that Task's execution workspace and
pins its first revision to the Task. The upload has no file, connector, secret,
workspace-sharing, or standing Skill-management authority. `playbook_id` and
`skill` are mutually exclusive.

Add `runtime` to an ordinary-key request using the runtime catalog receipt
above. Add `callback` to receive events only for this Task, or `cadence` to
create an existing Routine-backed follow-up for this long-horizon outcome:

```json
{
  "callback": {
    "url": "https://customer.example/hooks/organic-growth",
    "events": ["task.check_in", "task.requires_action", "task.done"],
    "secret": "a-customer-secret-with-at-least-16-characters"
  },
  "cadence": {
    "cron": "0 9 * * 1",
    "timezone": "America/Los_Angeles",
    "title": "Weekly organic-growth follow-up"
  }
}
```

The callback secret is accepted for delivery signing but never returned or
included in a Task projection. `cadence` uses a five-field cron expression and
the existing Routine owner; it is not a public scheduler or a second Task
lifecycle. A published-method customer may supply a callback, but cannot pick
a cadence.

## Attach files the work must read

A Task often depends on something the customer already has: a brief, a
spreadsheet, a design, a transcript, last quarter's report. Send the bytes
first, then create the Task that references them.

Bytes never travel through the create request. `POST /uploads` returns a signed
URL you PUT the file to directly, so an attachment is bounded by the 200 MB
object-storage limit rather than by the platform's 32 MiB inbound request
limit. The hash you claim is not trusted: finalize re-reads the stored object
and verifies size and sha256 before the file exists as far as the API is
concerned.

```bash
# 1. Ask where to put it. sha256 is the hex digest of the file's bytes.
curl -sS https://tycoon.us/api/public/uploads \
  -H "Authorization: Bearer $TYCOON_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"q3-brief.pdf","mime_type":"application/pdf","byte_size":184320,"sha256":"<hex>"}'
# → {"status":"upload","uploadUrl":"https://…","headers":{…},"expiresAt":"…"}
#   {"status":"exists","asset":{…}}      already stored — skip to step 3
#   {"status":"needs-finalize"}          bytes are there, finalize them

# 2. PUT the bytes with EXACTLY the headers you were given.
curl -sS -X PUT "$UPLOAD_URL" -H "Content-Type: application/pdf" --data-binary @q3-brief.pdf

# 3. Register it. Returns the asset id you reference when creating the Task.
curl -sS https://tycoon.us/api/public/uploads/finalize \
  -H "Authorization: Bearer $TYCOON_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"q3-brief.pdf","mime_type":"application/pdf","byte_size":184320,"sha256":"<hex>"}'
# → 201 {"asset":{"id":"cxxx","name":"q3-brief.pdf", …}}
```

Then reference the ids on create, up to 20 per Task:

```json
{
  "prompt": "Turn the attached brief into a launch plan.",
  "attachments": [{ "asset_id": "cxxx" }]
}
```

Both upload endpoints use the `tasks:write` scope — an attachment exists to
feed a Task, and no separate grant stands between a key and its own input.

**What the worker sees.** The Task's brief lists every attachment by id, name,
size and type, and names the commands that open them. A Task created with a
file it cannot read must say so in its result rather than quietly producing an
outcome that ignored the customer's input.

**Which workspace the file lands in.** The asset is created in the key's own
workspace. When the Task executes somewhere else — the isolated task-bound
workspace a create without `workspace_id` mints, or a bound workspace — the
same stored object is registered in that workspace too, so the worker can read
it. The bytes are stored once; only the reference is duplicated.

**Errors.** `404 attachment_not_found` — the id does not exist, or belongs to a
workspace this key does not control; the two are deliberately indistinguishable.
`409 blob_missing | size_mismatch | hash_mismatch` — finalize verified the
stored object and it did not match what you declared.

## Read status, result, and usage

```bash
curl -sS https://tycoon.us/api/public/tasks/cm... \
  -H "Authorization: Bearer $TYCOON_API_KEY"
```

```json
{
  "task": {
    "id": "cm...",
    "reference": "acme-42",
    "title": "Competitor research",
    "status": "completed",
    "created_at": "2026-08-01T12:00:00.000Z",
    "started_at": "2026-08-01T12:00:03.000Z",
    "completed_at": "2026-08-01T12:04:21.000Z",
    "updated_at": "2026-08-01T12:04:21.000Z"
  },
  "outcome": {
    "updated_at": "2026-08-01T12:04:21.000Z",
    "summary": "The experiment is live and the next weekly observation is scheduled.",
    "judgment": "unmet",
    "trajectory": "on-track",
    "remaining_gap": "The target lift is not yet proven.",
    "next": {
      "kind": "wait",
      "label": "Observe the weekly Metric sample",
      "at": "2026-08-08T12:00:00.000Z",
      "wait_for": "routine observation"
    },
    "measure": {
      "title": "Qualified organic clicks",
      "unit": "clicks",
      "current_value": 125,
      "baseline_value": 100,
      "baseline_at": "2026-08-01T12:00:00.000Z",
      "target_value": null,
      "last_synced_at": "2026-08-01T12:00:00.000Z"
    },
    "cadences": [{
      "purpose": "measurement",
      "status": "active",
      "cadence": "weekly",
      "next_run_at": "2026-08-08T12:00:00.000Z",
      "health": "healthy"
    }]
  },
  "playbook": {
    "id": "<existing-company-skill-id>",
    "revision": "<pinned-skill-revision-id>",
    "name": "Growth operating method"
  },
  "result": {
    "text": "...",
    "created_at": "2026-08-01T12:04:20.000Z"
  },
  "required_actions": [],
  "usage": {
    "input_tokens": 12340,
    "output_tokens": 1870,
    "cached_input_tokens": 4200,
    "cost_usd_micros": "482000",
    "cost_usd": 0.482,
    "ledger_entries": 3,
    "wallet_debit_applied": true,
    "budget_usd_micros": "25000000"
  },
  "poll_after_ms": null
}
```

Statuses are `queued`, `running`, `requires_action`, `waiting`, `completed`,
or `canceled`. Before a terminal state, honor `poll_after_ms`. If
`required_actions` is non-empty, read its `id`, `action_version`, safe `items`,
and `api_resolvable` flag, then follow *Resolve a required action from your
backend*.

The response deliberately omits workspace memory, system prompts, model
reasoning, agent/pod/run identities, child execution traces, and raw Task
metadata. Usage includes the API root and any direct child Tasks Tycoon Agent used to
complete it, so delegated work is not silently under-counted.

For a completed Success-Brief Task, `result.text` is the accepted result summary
from the exact current `Task.currentAcceptedEvaluationId`, projected through the
same communication snapshot used by Tasks and Tycoon Agent. Plain and historical Tasks
fall back to the durable close Comment selected by `TaskResult.closeCommentId`.
A later Comment, stale evaluation, or invalid/cross-Task pointer cannot silently
replace the published result.

## Pause, resume, or cancel a Task

Use the canonical lifecycle control adapter when the customer needs to stop or
resume work. It requires `tasks:write` and an `Idempotency-Key`.

```bash
curl -sS -X POST https://tycoon.us/api/public/tasks/cm.../control \
  -H "Authorization: Bearer $TYCOON_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: organic-growth-pause-1" \
  -d '{"action":"pause"}'
```

Actions are `pause`, `resume`, and `cancel`. Pause/resume use the existing
archive/restore fence; cancel uses the existing Task end owner, terminal
notification, timing, and dependent-Task wake path. There is no API-only Task
status. A same-body replay returns the same accepted response with
`idempotent_replay: true`.

## Next steps

- [Long-running outcomes](/docs/long-running-outcomes)
- [Messages, actions, and approvals](/docs/actions-approvals)
- [Idempotency and errors](/docs/reliability-errors)
