# Set up through the API

> Install or create the agent, create the project, post the app's pages and draft journeys through the API, from a coding agent on the MCP server or with an organization key and curl.

The API does for an app what the dashboard does: it installs or creates the agent, creates the project with its publishable key, posts the app's pages to the page catalog, and drafts journeys. A coding agent connected to Super Assist's MCP server calls it as you. Without the MCP server, you or the agent call it with an organization key and curl, from the app's repository.

The commands call `https://superassist.app`. When the app's runtime sets `host` to another Super Assist origin, call that origin instead. The [API reference](https://superassist.app/docs/api) lists every route and field.

## Call the API from a coding agent

The MCP server at `https://superassist.app/mcp` lets a coding agent call the API as you, in the organization you picked when you signed in. [Connect a coding agent](https://superassist.app/how-to/give-agents-the-docs.md) installs it with the Super Assist plugin in Claude Code, or adds it by URL in Codex, Cursor and VS Code. The Super Assist skill's `setup` and `sync` commands then run this page's steps for you.

The server has two tools:

- `search` looks up a route's parameters, body and answer in the API's description.
- `execute` runs the agent's code in a sandbox whose `request({ method, path, query, body })` calls a `/api/v1` route and returns its JSON answer. A refused call throws an error naming the method, path and status, with the API's `code`, `message` and `details`.

The code is an async arrow function that returns its result, calling `codemode.request()`. One `execute` that lists the projects and reads the first one's page catalog:

```js
async () => {
  const { data } = await codemode.request({ method: "GET", path: "/api/v1/projects" });
  return codemode.request({ method: "GET", path: `/api/v1/projects/${data[0].id}/pages` });
};
```

The routes, bodies and answers are the ones below. Each `execute` can make several calls, and Claude Code shows its code before it runs. The organization key and the `curl` commands are the way in for an agent without the MCP server, or for you in a terminal.

## Put an organization key in `.env.local`

An organization key calls the API as your organization. Only owners and admins see the **API keys** page that creates one.

1. In the dashboard, open **API keys**, name the key, for example `Coding agent`, and press **Create key**.
2. Copy the key. It starts with `sa_`, and the page shows it only once.
3. Add it to `.env.local` in the app's repository, without quotes:

   ```sh title=".env.local"
   SUPER_ASSIST_API_KEY=sa_...
   ```

4. Check that git ignores the file:

   ```sh
   git check-ignore .env.local
   ```

   It prints `.env.local` when the file is ignored. When it prints nothing, add `.env.local` to `.gitignore` before you go on.

## Send the key with every command

Each command reads the key from `.env.local` and sends it as a bearer token, so the key stays out of your code, your shell history and the command's output:

```sh
key=$(sed -n 's/^SUPER_ASSIST_API_KEY=//p' .env.local)
curl -s "https://superassist.app/api/v1/agents?limit=100" -H "Authorization: Bearer $key"
```

Keep `-v` off: it prints the `Authorization` header. A refused call answers with an error code and a message, such as:

```json
{
  "error": {
    "code": "agent_name_taken",
    "message": "An agent named \"saas-support\" already exists"
  }
}
```

`unauthorized` means the key is wrong or was revoked. A long JSON body is easier to write to a file and send with `--data-binary @file.json`.

## Pick the agent

The project needs an agent to answer. Reuse one, install a template, or create one, in that order:

1. List the organization's agents with the command above. The answer is `{ "data": [...], "nextCursor": null }`, each agent with its `id` and `name`. When one is named after the app, keep its `id` and go on to [Create the project](#create-the-project).
2. Otherwise, list the templates and install the one that fits the app. A template's id is its name:

   ```sh
   key=$(sed -n 's/^SUPER_ASSIST_API_KEY=//p' .env.local)
   curl -s https://superassist.app/api/v1/templates -H "Authorization: Bearer $key"
   ```

   ```sh
   key=$(sed -n 's/^SUPER_ASSIST_API_KEY=//p' .env.local)
   curl -s -X POST https://superassist.app/api/v1/templates/saas-support/install \
     -H "Authorization: Bearer $key"
   ```

   The install answers `201` with the agent, named after the template. It creates the template's skills and databases too, reusing any skill your organization already has by name. An agent or database of the same name answers `409`, and a plan limit `402`.

3. Otherwise, create an agent. Its name takes lowercase letters, numbers and single hyphens:

   ```sh
   key=$(sed -n 's/^SUPER_ASSIST_API_KEY=//p' .env.local)
   curl -s -X POST https://superassist.app/api/v1/agents \
     -H "Authorization: Bearer $key" -H "Content-Type: application/json" \
     -d '{"name":"my-shop","description":"Helps the shop team with orders and customers.","instructions":"You help the team of My shop work through their orders and customers on the open page."}'
   ```

   It answers `201` with the agent, on the default model.

## Fill the template's placeholders

An installed template's skills carry bracketed placeholders, such as `[billing page]`, listed under the template's `placeholders` in the templates answer. [Templates](https://superassist.app/dashboard/templates.md) says what each of `saas-support`'s stands for.

1. List the skills with `GET /api/v1/skills?limit=100`, and find each placeholder in their `instructions`.
2. Replace it with what the app's repository says, and send the skill's whole instructions back:

   ```sh
   key=$(sed -n 's/^SUPER_ASSIST_API_KEY=//p' .env.local)
   curl -s -X PATCH https://superassist.app/api/v1/skills/<skill-id> \
     -H "Authorization: Bearer $key" -H "Content-Type: application/json" \
     --data-binary @skill.json
   ```

   Here `skill.json` holds `{ "instructions": "..." }`.

3. Save the agent with an empty body, so its threads pick up the edited skills:

   ```sh
   key=$(sed -n 's/^SUPER_ASSIST_API_KEY=//p' .env.local)
   curl -s -X PATCH https://superassist.app/api/v1/agents/<agent-id> \
     -H "Authorization: Bearer $key" -H "Content-Type: application/json" -d '{}'
   ```

Leave a placeholder the repository doesn't answer, and note it for whoever finishes in the dashboard.

## Create the project

Create the project with the agent as its default and the origins the app runs on in development:

```sh
key=$(sed -n 's/^SUPER_ASSIST_API_KEY=//p' .env.local)
curl -s -X POST https://superassist.app/api/v1/projects \
  -H "Authorization: Bearer $key" -H "Content-Type: application/json" \
  -d '{"name":"My shop","defaultAgentId":"<agent-id>","allowedOrigins":["http://localhost:3000"]}'
```

`allowedOrigins` replaces the default list, so name every origin. Left out, the project allows `http://localhost:3000` and `http://localhost:5173`. The answer is `201` with the project:

```json
{
  "id": "7c1e4b0a-…",
  "name": "My shop",
  "description": null,
  "publishableKey": "pk_…",
  "allowedOrigins": ["http://localhost:3000"],
  "defaultAgentId": "<agent-id>",
  "createdBy": null,
  "createdAt": "2026-11-01T10:00:00.000Z",
  "updatedAt": "2026-11-01T10:00:00.000Z"
}
```

Write `publishableKey` to the app's public variable in `.env.local`, such as `NEXT_PUBLIC_SUPER_ASSIST_KEY` for Next.js or `VITE_SUPER_ASSIST_KEY` for Vite. Unlike the organization key, it ships in the app's bundle. [Use the hosted runtime](https://superassist.app/how-to/use-the-hosted-runtime.md) connects the app with it.

When the app already has a project, find its `id` in `GET /api/v1/projects`, by the `publishableKey` the app uses.

## Post the app's pages

The page catalog holds the app's pages for the journey editor's pickers. Pages report themselves once the app runs; posting them now puts the whole sitemap in the catalog from the start. Send one entry per `destinations` path, with the page's name, its instructions, and the actions and fields it lends:

```sh
key=$(sed -n 's/^SUPER_ASSIST_API_KEY=//p' .env.local)
curl -s -X POST https://superassist.app/api/v1/projects/<project-id>/pages \
  -H "Authorization: Bearer $key" -H "Content-Type: application/json" \
  --data-binary @pages.json
```

```json title="pages.json"
{
  "pages": [
    {
      "path": "/orders",
      "name": "Orders",
      "instructions": "Every order of the shop. Ship or cancel an order that is waiting to ship by its id.",
      "elements": [
        { "name": "new_order", "kind": "action", "label": "Open the New order dialog" },
        { "name": "ship_order", "kind": "action", "byId": true }
      ]
    },
    {
      "path": "/orders/:id",
      "name": "Order",
      "elements": [
        { "name": "note", "kind": "field", "label": "The order's note" },
        { "name": "save_note", "kind": "action", "label": "Save the note" }
      ]
    }
  ]
}
```

- `byId: true` marks a name that rows lend, each with its own `id`. It keeps no label.
- A page sent without `instructions` keeps those it has.
- One request takes 1 to 100 pages and at most 2,000 elements.

The answer is the whole catalog, `{ "data": [...] }`, and the project's **Pages** tab shows it.

A post only adds and updates. Once the app's routes change, add `"replace": true` beside `pages` and send every page the code has: the catalog then keeps only what you sent. The Super Assist skill's `sync` command builds that body from the code and sends it. Pages you left out go with their elements, those the app reported itself included, and so do the elements a page you sent leaves out. When a page you dropped by mistake still lends something, the app reports it back the next time an end user opens it.

## Draft journeys

A journey walks end users through the app toward a goal, one step at a time. Start with up to three, each of three or four steps, built from the app's routes and what its pages lend:

```sh
key=$(sed -n 's/^SUPER_ASSIST_API_KEY=//p' .env.local)
curl -s -X POST https://superassist.app/api/v1/projects/<project-id>/journeys \
  -H "Authorization: Bearer $key" -H "Content-Type: application/json" \
  --data-binary @journey.json
```

```json title="journey.json"
{
  "name": "ship-an-order",
  "goal": "A new team member ships an order and leaves a note on it.",
  "trigger": "manual",
  "steps": [
    {
      "path": "/orders",
      "title": "Your orders",
      "body": "Every order of the shop is listed here."
    },
    {
      "path": "/orders",
      "element": { "kind": "action", "name": "ship_order" },
      "title": "Ship an order",
      "body": "Press Ship on an order that is waiting to ship."
    },
    {
      "path": "/orders/:id",
      "element": { "kind": "field", "name": "note" },
      "title": "Leave a note",
      "body": "Each order keeps a note for your team."
    },
    {
      "path": "/orders/:id",
      "element": { "kind": "action", "name": "save_note" },
      "title": "Save it"
    }
  ]
}
```

- `name` follows the same rule as an agent's and is unique in the project.
- A step's `path` is a `destinations` path, and its optional `element` is an action or field that page lends, by its lent name. The API doesn't check either against the catalog, so take them from the pages you posted.
- A journey has 1 to 10 steps. A `title` takes up to 120 characters, a `body` up to 1,000.
- `manual` starts the journey only from host code or a link. `page` starts it once per end user on its first step's page. A member can change it before publishing.

The answer is `201` with the journey, its `state` `draft` and its `publishedSteps` `null`: end users see nothing of it yet. A name already taken answers `409` `journey_name_taken`. To change a draft, send the changed fields to `PATCH /api/v1/projects/<project-id>/journeys/<journey-id>`.

## Publish the journeys

Only a member publishes a journey. A coding agent on the MCP server acts as you, so it can publish a draft once you say so, with `POST /api/v1/projects/<project-id>/journeys/<journey-id>/publish`. The same route answers an organization key with `403`: on the project's **Journeys** page, set each draft's trigger and publish it.

The key stays in `.env.local` for later drafts. When you no longer need it, press **Revoke** beside it on **API keys**.
