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

# Set up a project from code or an agent

> Create a project, issue its keys, connect a worker and operate runs from the SDK or an MCP client.

Everything a person does in the console to run workflows, a program or an AI
agent can do too: create the project, issue its keys, connect a worker and
operate its runs. Each step below names the MCP tool and the SDK method that
do it: steps 1, 2 and 11 and `projects.limits` on the management client,
the rest on the project client.

| Surface | Where | Signs in with |
| - | - | - |
| Management MCP | `https://<console origin>/mcp` | An OAuth access token: `duraton:read` reads, `duraton:write` also changes |
| Management SDK | `createManagementClient({ baseUrl, auth })` from `@duraton/sdk/management` | The same OAuth token |
| Project API | `createClient({ url, apiKey })` from `@duraton/sdk/client`, `url` the project's `https://<project slug>.<project domain>/workflows/v1` | A secret key (`dtn_sk_`) |
| Worker | `connect({ url, apiKey, app, workflows })` from `@duraton/sdk` | A worker key (`dtn_wk_`) or a secret key |

The `workflows_*` tools work on the management MCP, and on the project's own
MCP (`https://<project slug>.<project domain>/mcp`) with a secret key. There
the tools that change something are offered only once the project's operator
has switched MCP writes on.

## Steps

| # | Step | MCP tool | SDK |
| - | - | - | - |
| 1 | Create a project with workflows on | `project_create_project` with `services: ["workflows"]` | `projects.create({ workspaceId, name, services: ["workflows"] })` |
| 1a | Or switch workflows on for a project you have | `project_update_project_services` (send the whole set) | `projects.setServices(id, [...])` |
| 2 | Issue a worker key for your worker, and a secret key for your server code | `project_create_key` with `kind: "worker"` or `"secret"`, `name` optional; it answers no token | `keys.create(projectId, "worker", { name })` |
| 3 | Connect a worker: it registers its workflows | (worker code) | `connect({ url, apiKey, app, workflows })` |
| 4 | Check the worker and its workflows | `workflows_list_runners`, `workflows_list_workflows`, `workflows_get_workflow` | `runners.list()`, `workflows.list()`, `workflows.get(app, name)` |
| 5 | Read the limits and what they measure now | `workflows_get_limits`, `project_get_limits` | `limits.get()`, `projects.limits(id)` |
| 6 | Start runs and send events | `workflows_run_workflow`, `workflows_send_event` | `workflows.start(app, name, { input })`, `events.send({ name, data })` |
| 7 | Follow runs | `workflows_list_runs`, `workflows_get_run`, `workflows_list_steps` | `runs.list(...)`, `runs.get(id)`, `runs.steps(id)`, `runs.watchTransitions(...)` |
| 8 | Cancel and replay | `workflows_cancel_run`, `workflows_cancel_runs`, `workflows_replay_run`, `workflows_replay_runs` | `runs.cancel`, `runs.bulkCancel`, `runs.replay`, `runs.bulkReplay` |
| 9 | Schedule a workflow | `workflows_create_schedule` (`paused` optional), `workflows_update_schedule`, `workflows_pause_schedule`, `workflows_resume_schedule`, `workflows_delete_schedule`, `workflows_list_schedules` | `schedules.create`, `update`, `pause`, `resume`, `delete`, `list`, `get` |
| 10 | Wake a run waiting on outside input | `workflows_list_tokens`, `workflows_complete_token` | `tokens.list`, `tokens.get`, `tokens.complete` |
| 11 | List, rotate and revoke keys | `project_list_keys`, `project_rotate_key`, `project_revoke_key` | `keys.list(projectId)`, `keys.iterate(projectId)`, `keys.rotate(keyId)`, `keys.revoke(keyId)` |

## Keys

* On the SDK, a key's token is in the answer that issued it and never again.
  A retry with the same `Idempotency-Key` (SDK `{ idempotencyKey }`) answers
  the same key with `token: null`, so keep the token from the first answer, as
  a secret.
* The MCP tools never answer a token: `project_create_key` and
  `project_rotate_key` answer the key's id, kind, name and prefix with
  `token: null`, first and on a retry. A person who needs the token issues or
  rotates the key in the console or with the SDK.
* A key may carry a `name`, up to 256 characters, shown in the key list.
* Rotating a key issues a new key of the same kind and name, its token shown
  once as on issue (never by MCP), and revokes the old key in the same
  change: update every place that uses the old token first. A revoked key cannot be rotated
  (`409 conflict`).
* Issuing, listing, rotating and revoking keys needs the project's admin role.
* A worker key may only connect a worker. Keep secret and worker keys out of
  browser and mobile code; a publishable key reaches nothing under
  `/workflows/v1`.
* A revoked key is refused from then on, and a worker connected with it is
  disconnected.

## What an agent cannot change

Limits and settings come from the project's plan and its operator: read them
with `workflows_get_limits`, never set them through these surfaces. Switching
the project MCP's write tools on is the operator's choice too.

Every error is a problem document with a stable `code`; the codes are listed
in the [`/workflows/v1` reference](https://docs.duraton.dev/workflows/rest-reference).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.