---
title: niyra_execute — long-running tasks
description: Tell Niyra to actually do something. Spawn-on-timeout pattern keeps short tasks synchronous and long ones polled.
url: /docs/api-tool-niyra-execute
lastUpdated: 2026-06-11
---

# niyra_execute — long-running tasks


# niyra_execute

The "do this for me" tool. Unlike `niyra_ask`, `niyra_execute` is allowed to invoke Niyra's full tool registry — email, calendar, web search, integrations, code execution, you name it.

## When to use

- "Email Sarah and ask if next Tuesday at 3pm works for the kickoff."
- "Pull the Linear tickets tagged P0 and summarize them."
- "Research recent funding rounds in Series B vertical SaaS and draft a brief."

If the request is just "what is X" — that's `niyra_ask`.

## Endpoint

| Method | Path | Auth | Scope |
| ------ | ---- | ---- | ----- |
| POST   | `/v1/public/execute` | Bearer token (PAT or OAuth) | `niyra:execute` |

MCP equivalent: `POST /mcp` with method `tools/call`, name `niyra_execute`.

## Request

```json
{
  "instruction": "Email sarah@acme.com and propose Tuesday 3pm for the kickoff.",
  "conversation_id": "optional-uuid",
  "approval_mode": "auto",
  "channel_context": "from my CI pipeline"
}
```

| Field | Type | Required | Notes |
| ----- | ---- | -------- | ----- |
| `instruction` | string | yes | What you want Niyra to do. Max 8000 chars. |
| `conversation_id` | uuid | no | Pin to a thread for context continuity. |
| `approval_mode` | enum | no | `auto` (default — Niyra decides), `dry_run` (plan only, no side effects), `confirm` (return a plan; second call with `confirmed_task_id` to execute) |
| `channel_context` | string | no | Where this request came from — colors Niyra's tone. |

## Response — synchronous case

Returned when the work finishes inside the 60-second window:

```json
{
  "result": "Sent. Sarah replied that Tuesday works — confirmed for 3pm.",
  "conversation_id": "8f3b…",
  "task_id": "task_xyz",
  "tools_used": ["gmail_send", "gmail_read"],
  "elapsed_ms": 8420,
  "terminal": true
}
```

## Response — spawn-on-timeout case

When the work crosses 60 seconds, the same request returns:

```json
{
  "result": null,
  "conversation_id": "8f3b…",
  "task_id": "task_xyz",
  "status": "running",
  "elapsed_ms": 60000,
  "terminal": false
}
```

Poll `GET /v1/public/tasks/{task_id}` until `terminal: true`. The task keeps running on Niyra's side regardless of whether you poll — losing the connection doesn't lose the work.

## Code examples

### curl

```bash
curl -X POST https://api.niyra.ai/v1/public/execute \
  -H "Authorization: Bearer pat_…" \
  -H "Content-Type: application/json" \
  -d '{
    "instruction": "Summarize the Linear tickets tagged P0 this week."
  }'
```

### JavaScript with polling

```js
async function niyraExecute(instruction, token) {
  const start = await fetch("https://api.niyra.ai/v1/public/execute", {
    method: "POST",
    headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
    body: JSON.stringify({ instruction }),
  }).then((r) => r.json());

  if (start.terminal) return start.result;

  // Poll every 3s, give up after 10 minutes.
  for (let i = 0; i < 200; i++) {
    await new Promise((r) => setTimeout(r, 3000));
    const tick = await fetch(
      `https://api.niyra.ai/v1/public/tasks/${start.task_id}`,
      { headers: { Authorization: `Bearer ${token}` } }
    ).then((r) => r.json());
    if (tick.terminal) return tick.result;
  }
  throw new Error("Task did not terminate within 10 minutes");
}
```

## Approval modes

| Mode | What happens |
| ---- | ------------ |
| `auto` | Niyra plans and executes in one shot. Default. |
| `dry_run` | Returns the plan Niyra *would* execute, with no side effects. Useful for previewing before committing. |
| `confirm` | Returns a plan with a `pending_task_id`. Send a second `execute` call with `confirmed_task_id: <id>` to run it. |

## Errors

| Status | Code | Meaning |
| ------ | ---- | ------- |
| 400 | `invalid_request` | Empty instruction, too long, or invalid approval_mode |
| 401 | `invalid_token` | Token revoked/expired/unknown |
| 403 | `insufficient_scope` | Token lacks `niyra:execute` |
| 403 | `tool_disabled` | Niyra refused to run a tool the user has disabled in Settings → Skills |
| 429 | `rate_limit_exceeded` | Per-token budget exhausted |

## Related

- [niyra_get_task](/docs/api-tool-niyra-get-task) — poll a spawned task
- [niyra_ask](/docs/api-tool-niyra-ask) — for "what is X" queries
- [Scope catalog](/docs/api-scopes)
- [Rate limits](/docs/api-rate-limits)
