> For the complete documentation index, see [llms.txt](https://docs.lleverage.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.lleverage.ai/build-and-improve/automate/publish-apps-and-apis/rest-api.md).

# REST API

Use Project API tokens to call Lleverage's versioned REST API from services, scripts and other programmatic clients.

Lleverage exposes a versioned REST API under `https://app.lleverage.ai/api/v1`. Use it when a service, script or other programmatic client needs to work with resources inside a Project.

This is different from a published Workflow **API Endpoint**. A published API Endpoint is an invocation surface for one Workflow. The `/api/v1` REST API is the broader Project-scoped programmatic surface used to inspect and use supported Lleverage resources.

## Authenticate with a Project API token

Send a Project API token as a bearer token on every request:

```bash
curl -H "Authorization: Bearer $LLEVERAGE_TOKEN" \
  https://app.lleverage.ai/api/v1/workflows
```

Create and revoke tokens under **Control → API Tokens**. A Project token is scoped to the Project it was created for; the current Project-token surface does not expose narrower per-route scopes.

{% hint style="info" %}
See [API Tokens](/administer-and-govern/control/api-tokens.md) for the current token permission, expiry, creator-membership and rotation behaviour.
{% endhint %}

## Useful resource families

The public v1 surface includes Project-scoped APIs for resources such as:

* **Workflows** — list Workflows, inspect versions and executions, and start runs;
* **Files** — obtain upload and download URLs for Workflow files;
* **Knowledge Bases** — work with Knowledge Bases and document ingestion;
* **Members** — inspect Project membership and roles;
* **API Tokens** — inspect token inventory, identify the current token and revoke tokens;
* **Requests** — read human-in-the-loop Requests and their saved views.

Other Project surfaces are exposed under v1 as well. Treat the API contract as versioned: do not depend on undocumented internal routes outside `/api/v1`.

## List Workflows

`GET /api/v1/workflows` returns Workflows in the token's Project.

```bash
curl -s \
  -H "Authorization: Bearer $LLEVERAGE_TOKEN" \
  https://app.lleverage.ai/api/v1/workflows
```

Project scoping happens server-side. A resource outside the token's Project is generally returned as `404`, so a not-found response can also mean the resource is outside the token's scope.

## Run a Workflow

Start a Workflow with:

```
POST /api/v1/workflows/{workflowId}/run
```

The request accepts an `inputs` object. Runs default to the **test** environment; pass `"environment": "production"` when you intend to run the active published production version.

```bash
curl -X POST \
  -H "Authorization: Bearer $LLEVERAGE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "environment": "production",
    "inputs": {
      "orderId": "12345"
    }
  }' \
  https://app.lleverage.ai/api/v1/workflows/$WORKFLOW_ID/run
```

A production run requires a published production version. Do not send `versionId`, `sessionId`, `resumeId` or `uiNodeId` unless you are integrating against those specific contracts; they are not needed for the basic start-a-Workflow case.

A run response may be an event stream or a JSON response for outcomes resolved before streaming begins. Read the response `Content-Type` rather than assuming every successful or unsuccessful run response is an event stream.

## Delete a Workflow

```
DELETE /api/v1/workflows/{workflowId}
```

Deletes the Workflow, stops its triggers and schedule, and deletes the Requests it raised. This cannot be undone through the API.

A Workflow that nothing uses is deleted by the request alone:

```bash
curl -X DELETE \
  -H "Authorization: Bearer $LLEVERAGE_TOKEN" \
  https://app.lleverage.ai/api/v1/workflows/$WORKFLOW_ID
```

```json
{ "workflowId": "wf-1", "deleted": true }
```

### Deleting a Workflow that is in use

A Workflow is **in use** when any of these is true:

* a published version is switched on;
* its schedule is on;
* a Skill that has not been deleted lists it;
* a Request it raised is waiting for an answer, or failed to resume and can still be answered again.

A switched-off version, a Request from a test run and a deleted Skill do not count, and neither does a run that is executing at that moment.

For a Workflow that is in use, the request above answers `409` and deletes nothing. The `error` text says what uses the Workflow and what deleting it does to each use, and `details.inUse` carries the same list:

```json
{
  "error": "This workflow is in use, so it was not deleted. Version 1.0.0 is switched on (API trigger): calls to it fail. Its schedule is on (0 9 * * 1, Europe/Amsterdam): no more scheduled runs start. 2 requests are waiting for an answer: they are deleted, and the runs waiting on them can never be resumed. To delete it anyway, repeat the request with confirmInUse=true.",
  "code": "conflict",
  "details": {
    "workflowId": "wf-1",
    "title": "Order intake",
    "inUse": [
      {
        "kind": "active_version",
        "versionId": "ver-1",
        "version": "1.0.0",
        "triggerType": "API_CALL",
        "description": "Version 1.0.0 is switched on (API trigger)",
        "consequence": "calls to it fail"
      },
      {
        "kind": "schedule",
        "cronExpression": "0 9 * * 1",
        "timezone": "Europe/Amsterdam",
        "description": "Its schedule is on (0 9 * * 1, Europe/Amsterdam)",
        "consequence": "no more scheduled runs start"
      },
      {
        "kind": "pending_requests",
        "count": 2,
        "description": "2 requests are waiting for an answer",
        "consequence": "they are deleted, and the runs waiting on them can never be resumed"
      }
    ]
  }
}
```

`code` is lower case here (`conflict`). `details.title` is the Workflow's title, shortened to 60 characters.

Each entry in `details.inUse` has a `kind`, a `description` and a `consequence`. The kinds are:

| `kind`                 | Other fields                          | Stands for                                                                          |
| ---------------------- | ------------------------------------- | ----------------------------------------------------------------------------------- |
| `active_version`       | `versionId`, `version`, `triggerType` | A published version that is switched on. `version` and `triggerType` can be `null`. |
| `schedule`             | `cronExpression`, `timezone`          | The Workflow's schedule, while it is on.                                            |
| `skill`                | `skillId`, `name`                     | A Skill that lists the Workflow. `name` is shortened to 60 characters.              |
| `pending_requests`     | `count`                               | Requests waiting for an answer.                                                     |
| `failed_requests`      | `count`                               | Requests whose run failed to resume and that can be answered again.                 |
| `more_active_versions` | `count`                               | Switched-on versions that are not listed one by one.                                |
| `more_skills`          | `count`                               | Skills that are not listed one by one.                                              |

The answer lists up to five switched-on versions and up to five Skills one by one, newest version first and Skills by name, and counts the rest in a `more_active_versions` or `more_skills` entry. It lists fewer when long names would make the answer too large. A Skill you are not allowed to read is counted in `more_skills` and never named.

Show the answer to whoever owns the integration or the Workflow, and repeat the request with `confirmInUse=true` only once they agree:

```bash
curl -X DELETE \
  -H "Authorization: Bearer $LLEVERAGE_TOKEN" \
  "https://app.lleverage.ai/api/v1/workflows/$WORKFLOW_ID?confirmInUse=true"
```

```json
{ "workflowId": "wf-1", "deleted": true }
```

| `confirmInUse`                                             | Result                                                                |
| ---------------------------------------------------------- | --------------------------------------------------------------------- |
| `true`                                                     | Deletes the Workflow even though it is in use.                        |
| `false`, or left out                                       | Deletes a Workflow that is not in use; answers `409` for one that is. |
| any other value (for example `yes`, `1` or an empty value) | `400`, and nothing is deleted.                                        |

The `400` answer has no `code`:

```json
{
  "error": "Invalid query",
  "details": {
    "formErrors": [],
    "fieldErrors": {
      "confirmInUse": ["Invalid option: expected one of \"true\"|\"false\""]
    }
  }
}
```

A Workflow that is not in use is deleted exactly as before, with or without the parameter, so a client that never deletes live Workflows sees no change.

### Discarding the draft of a Workflow that was never published

```
POST /api/v1/workflows/{workflowId}/discard-draft
```

Discarding the draft removes the Workflow as well when it has no published version. If that Workflow is in use, for example because a Skill lists it, the request answers `409` in the same shape, starting "This workflow was never published, so discarding its draft would remove it, and it is in use. Nothing was discarded." and ending "To discard the draft and remove the workflow anyway, repeat the request with confirmInUse=true." Repeat it with `?confirmInUse=true` to go ahead:

```json
{ "workflowId": "wf-1", "workflowDeleted": true }
```

`confirmInUse` takes the same values here, and any other value is a `400`. Discarding the draft of a Workflow that has a published version is not affected.

{% hint style="info" %}
The MCP tools `lleverage_delete-workflow` and `lleverage_discard-workflow-draft` have no `confirmInUse` input and never send it. For a Workflow that is in use they remove nothing: they answer with an error that lists what uses it and says the person can remove it from the Workflows list in the Lleverage app.
{% endhint %}

## Errors and retries

Authentication failures return `401`. Several invalid-token conditions intentionally collapse to the same `Token is invalid` response rather than exposing detailed authorisation state.

The public API does **not** currently provide general idempotency handling. Do not blindly retry state-changing requests. Repeating a mutating `POST` can repeat the operation, including starting a Workflow twice. For reads, normal client retry behaviour is safer; for writes, make retry decisions based on the specific operation and the response you received.

A `409` on a delete usually means the Workflow is in use; see [Deleting a Workflow that is in use](#deleting-a-workflow-that-is-in-use). A `409` without `details.inUse` means the Workflow is being published or switched on or off at that moment and nothing was deleted; repeat the request after a short wait.

## Keep the two API surfaces distinct

Use **REST API** when your integration needs Project-scoped programmatic access to Lleverage resources.

Use a published Workflow **API Endpoint** when another system only needs to invoke that published Workflow through the endpoint generated from its API Call trigger.

This distinction keeps the Project API contract separate from a Workflow's published invocation contract.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.lleverage.ai/build-and-improve/automate/publish-apps-and-apis/rest-api.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
