# Chat, tasks and workflows

> Ask Italic from your own code, decide its drafts, build workflows and set how connected apps ask first.

These endpoints do what [Chat](/chat), [Tasks](/tasks), [Workflows](/workflows)
and [Connectors](/connectors) do in the Italic apps, with the same checks and
limits. Each needs its own permission on the API key; see the
[permissions table](/quickstart#api-keys).

## Chat [#chat]

`POST /runs` asks Italic a question, as the apps' chat does. It needs
`agent:chat` and an `Idempotency-Key`:

```bash
curl https://app.italic.com/api/v2/runs \
  -H "Authorization: Bearer $ITALIC_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: ask-2026-10-22-1' \
  --data '{"message":"What did I promise Sam this week?","timeZone":"America/New_York"}'
```

The answer is a **run**, returned as `{"run": {…}}`:

```json
{
  "run": {
    "id": "8f3b0c6e-2d4a-4c1b-9a57-6e1d2f3a4b5c",
    "source": "chat",
    "status": "completed",
    "reply": "You told Sam you would send the pricing doc by Friday.",
    "actions": [],
    "questions": [],
    "createdAt": 1790121600000,
    "updatedAt": 1790121612000
  }
}
```

`run.reply` holds Italic's answer in Markdown once `run.status` is `completed`.
Italic searches the account's recordings and connected apps, and can draft an
email, an event or another action. A draft the account's permissions say to ask
about comes back in `run.actions` with `status: "proposed"`, and the run is
`awaiting_approval` until it is decided. The request answers when the reply is
ready, which can take a minute or more.

Italic keeps no conversation for API calls. To follow up, send the earlier turns
in `history`, oldest first. Reusing an `Idempotency-Key` with the same body
returns the first run with `replayed: true` instead of asking again; keys are
kept for 24 hours. If the first request is still running, the retry answers
`409 idempotency_in_progress` with `Retry-After: 5`: wait, then send it again.
An API key can send ten messages a minute.

Read a run again with `GET /runs/{id}`. `POST /runs/{id}/cancel` discards its
waiting drafts and ends it.

## Tasks [#tasks]

`GET /tasks` lists what waits for a decision, newest first, as Tasks does in
the apps. It answers `{"runs": […]}`. With `agent:chat` it lists runs from chat;
with `actions:review`, runs that workflows started (`source: "recording"`).

<Screenshot src="/images/tasks/tasks" width={1600} height={1250} alt="Tasks in the web app: a calendar event and a reminder drafted from Chat, and an email drafted by a workflow from a Clip, each under the words that started it with its approve, Edit and Discard buttons." caption="The same runs in Tasks on the web: two from Chat, one from a workflow." />

Approve or discard one draft with
`POST /runs/{id}/actions/{actionId}/decision`:

```json
{"decision": "approve", "tool": "send_email"}
```

`decision` is `approve` or `reject`; `reject` discards the draft and needs no
`tool`. Approving names the draft's `tool`, so you approve only what you showed
the person; a different tool answers `409 tool_mismatch`. Send `args` to approve an
email, event or reminder with edited fields. Italic checks the draft again as
the apps do: the app must still be on in Connectors and the fields valid.
Approving sends the email or adds the event, so read the returned action's
`status` (`succeeded`, `failed` or `uncertain`). A reminder is `approved`
until the account's iPhone adds it.

A workflow can ask a question instead, listed in `questions`. Answer it with
`POST /runs/{id}/questions/{questionId}/answer` and one of `choice` (an index),
`text`, or `dismiss: true`. This needs `agent:chat`.

## Workflows [#workflows]

`GET /workflows` and `GET /workflows/{id}` read workflows (`workflows:read`).
`PATCH /workflows/{id}` with `{"enabled": false}` turns one off
(`workflows:write`).

`POST /workflows` creates one and `PUT /workflows/{id}` replaces one
(`workflows:manage`). The body is what the [builder](/workflows/build) saves: a
`name`, a `trigger` and up to eight `actions`. Italic checks it exactly as the
builder does, and an invalid workflow answers `400` with the builder's message.

```json
{
  "name": "Remind me",
  "trigger": {"type": "recording", "phrases": ["remind me"], "match": "start"},
  "actions": [
    {"id": "s1", "type": "create_reminder", "approval": "ask",
     "title": "{{recording.command}}", "notes": "", "due": "", "list": ""}
  ]
}
```

With `"match": "start"`, `{{recording.command}}` is what the person said after
the phrase; with `"anywhere"` it is the whole transcript.

`POST /workflows` needs an `Idempotency-Key`. A **Send webhook** action with a
new `url` also needs `webhooks:manage`; the response's `secrets` holds its
signing secret, which is shown once (a replayed create shows it again).
`DELETE /workflows/{id}` deletes a workflow and stops webhooks no other workflow
sends to. An account can have 20 workflows.

## Connectors [#connectors]

`GET /connectors` lists the connected apps with their accounts, and
`permissions`: whether Italic asks first before it acts in each app (`ask`),
acts without asking (`allow`), or doesn't use it (`off`). It needs
`workflows:read` or `connectors:manage`.

With `connectors:manage`:

- `PUT /connectors/permissions/{tool}` sets an app's permission, such as
  `send_email` for email.
- `PUT /connectors/permissions/{tool}/accounts/{accountId}` gives one email,
  calendar or Google Drive account its own permission; `default` uses the
  app's again.
- `DELETE /connectors/accounts/{id}` disconnects a Google, Outlook, Notion or
  Slack account and revokes Italic's access where the provider allows it.

Connecting an account is a sign-in with the provider, so it happens in the
Italic apps, never with an API key. Linear, Granola and Ramp sign-ins and
custom connectors are also disconnected in the apps.

