---
title: Creating agents | Letta Docs
description: Create agents with memory, configure skills and dreaming, and understand agent and conversation IDs
---

Create a persistent agent with `client.createAgent()`. It returns the agent’s ID as a string, which you use to open sessions and manage the agent later.

```
import { LettaAgentClient } from "@letta-ai/letta-agent-sdk";


const client = new LettaAgentClient({
  backend: "cloud",
  apiKey: process.env.LETTA_API_KEY,
});


const agentId = await client.createAgent({
  model: "anthropic/claude-opus-4-8",
  persona:
    "You are Quinn, a digital research analyst who can inspect files, run commands, synthesize sources, and maintain living memory about the user's organization.",
  human:
    "The user prefers concise memos with evidence, caveats, and recommended next actions.",
});
// → "agent-3f97f111-3244-41a3-958f-63285dcb412d"
```

## Agents, conversations, and sessions

An **agent** is the persistent entity with memory. A **conversation** is a thread on that agent. A **session** is the active connection you use to send messages and stream events.

- Agent IDs look like `agent-xxx` on the cloud backend and `agent-local-xxx` on the local backend. Conversation IDs look like `conv-xxx`.
- `createAgent()` also creates the agent’s default conversation, so a new agent is immediately usable.
- `createSession(agentId)` starts a **new** conversation on an existing agent.
- `resumeSession(id)` accepts either kind of ID: pass an `agent-xxx` ID to resume the agent’s default conversation, or a `conv-xxx` ID to resume a specific conversation.

```
// New conversation on an existing agent
await using session = client.createSession(agentId);


// Later: save the thread and pick it up again
const conversationId = session.conversationId;
// → "conv-cff81390-0763-431d-b24e-b218ae37ec81"
await using resumed = client.resumeSession(conversationId);


// Or resume the agent's default conversation
await using main = client.resumeSession(agentId);
```

Sessions expose read-only `agentId`, `conversationId`, and `sessionId` values once the backend resolves them. You can also create and inspect conversations without opening a session through `client.conversations` (`create`, `list`, `retrieve`, `update`, `listMessages`).

## Memory

### Persona and human shorthand

`persona` and `human` are conveniences that set the agent’s persona and human memory blocks:

```
const agentId = await client.createAgent({
  persona: "You are a resident engineering teammate for this repository.",
  human: "The user prefers practical handoffs with commands to run.",
});
```

### Memory blocks

Pass `memory` for full control. Each item is either a new block or a reference to an existing shared block:

```
const agentId = await client.createAgent({
  model: "anthropic/claude-opus-4-8",
  memory: [
    {
      label: "persona",
      value: "You are Quinn, a digital research analyst.",
    },
    {
      label: "team-context",
      value: "The team ships weekly on Thursdays. Staging is at stage.example.com.",
    },
    { blockId: "block-abc123" }, // attach an existing shared block
  ],
});
```

Use object blocks with `label` and `value`; current creation backends reject memory preset names. See [Shared memory](/agent-sdk/repositories/index.md) for sharing files and working context between Cloud agents.

### MemFS

Every agent has a [MemFS](/concepts/memfs/index.md), the git-backed memory filesystem where it stores long-term memory as Markdown files. It is enabled by default; pass `memfs: false` for worker-style agents that do not need durable memory (enabling MemFS adds a backend round trip at creation, and concurrent sessions on a shared-MemFS agent contend on its git state).

Where MemFS lives depends on the backend:

| Backend                                        | MemFS location                                                            |
| ---------------------------------------------- | ------------------------------------------------------------------------- |
| `local` (default)                              | `~/.letta/lc-local-backend/memfs/<agentId>/memory` on the current machine |
| `local` with `appServer.harnessBackend: "api"` | `~/.letta/agents/<agentId>/memory` on the current machine                 |
| `cloud`                                        | Hosted in Letta Cloud; checked out on the computer executing the session  |

On the local backend, override the root with the `MEMORY_DIR` or `LETTA_MEMORY_DIR` environment variable via the session’s `env` option. On the cloud backend, find the checkout path on the executing computer with `(await session.getDeviceStatus()).memoryDirectory`.

## Skills

[Skills](/configuration/skills/index.md) are packaged instructions the agent can load for particular kinds of tasks. A skill is a directory containing a `SKILL.md` with frontmatter (`name`, `description`), optionally alongside `scripts/`, `references/`, and `templates/`.

### Skill sources

Control which sources a session exposes with `skillSources`, at agent creation or per session:

```
type SkillSource = "bundled" | "global" | "agent" | "project";


const agentId = await client.createAgent({
  skillSources: ["bundled", "agent"],
});


// Or per session; pass [] to disable skills entirely
await using session = client.createSession(agentId, {
  skillSources: [],
});
```

Each source loads from a specific place on the machine running the harness:

| Source    | Location                                              | Lifetime and scope                                        |
| --------- | ----------------------------------------------------- | --------------------------------------------------------- |
| `bundled` | Shipped with the harness package                      | Read-only; excluded skills vary by backend                |
| `global`  | `~/.letta/skills/`                                    | All agents on that machine                                |
| `agent`   | The agent’s MemFS under `skills/`                     | Travels with the agent; versioned; syncs for cloud agents |
| `project` | `<cwd>/.agents/skills/` (and legacy `<cwd>/.skills/`) | Anything working in that directory                        |

When the same skill ID appears in multiple sources, the more specific one wins: `project` > `agent` > `global` > `bundled`.

“The machine running the harness” depends on the backend. With `backend: "local"`, that’s your machine. With `backend: "cloud"`, global and project directories live on the executing computer — the managed sandbox or your connected computer — not on the SDK host.

### Give an agent its own skills

Agent-owned skills live in the agent’s MemFS under `skills/<name>/SKILL.md`, so they are versioned with the agent’s memory and follow it across machines. Install one from GitHub, a registry, or a URL with the Letta CLI:

```
# GitHub repo, tree URL, SKILL.md URL, or owner/repo/path shorthand
letta skills install anthropics/skills/pdf --agent agent-abc123


# ClawHub registry
letta skills install clawhub/nano-banana-pro --agent agent-abc123
```

The install is committed to the agent’s MemFS git repository and, for cloud agents, synced back to Letta. Agents can also install skills for themselves (the bundled `acquiring-skills` skill teaches them how), or simply write `skills/<name>/SKILL.md` into their memory directly. New skills are discovered on the next message.

### Use skills from `npx skills` or your repository

Vercel’s [`npx skills add`](https://github.com/vercel-labs/skills) installs skills into the cross-tool `.agents/skills/` project directory — which is exactly what Letta’s `project` source reads, so those skills work in sessions whose `cwd` is that project with no extra configuration:

```
npx skills add vercel-labs/agent-skills
```

The same applies to skills you commit to a repository yourself: put them under `.agents/skills/<name>/SKILL.md` and any session working in that checkout picks them up. Letta does not read other tools’ agent-specific directories (`.claude/skills/`, `~/.config/agents/skills/` — where `npx skills add -g` installs), so use the universal `.agents/skills/` project directory, or copy to `~/.letta/skills/` for machine-wide skills.

## Dreaming

[Dreaming](/configuration/memory/index.md) uses background subagents to review recent conversations, consolidate lessons, and update memory without interrupting active work. Configure it with `dreaming`:

```
const agentId = await client.createAgent({
  persona: "You are a support engineer who learns each customer's environment.",
  dreaming: {
    trigger: "step-count", // "off" | "step-count" | "compaction-event"
    behavior: "auto-launch", // "reminder" | "auto-launch"
    stepCount: 25,
  },
});
```

- `trigger` controls when dreaming runs: after a number of steps, on context compaction, or never.
- `behavior` controls what happens at the trigger: remind the agent to update memory, or automatically launch a background dreaming subagent. It can only be set at agent creation.
- `stepCount` is the step interval for the `"step-count"` trigger.

Sessions can override `trigger` and `stepCount` (but not `behavior`) with the session `dreaming` option. The `init` message reports the effective settings for a session.

## Other creation options

```
const agentId = await client.createAgent({
  name: "quinn-research",
  description: "Research analyst for the growth team",
  tags: ["research", "growth"],
  hidden: true, // worker/subagent semantics: hidden from default listings
  baseTools: ["web_search"], // server-side tools; defaults to web_search and fetch_webpage, [] for none
  cwd: "/workspace/project",
});
```

### System prompts

Keep the default system prompt for most agents. It teaches the agent how to use the harness, including tools and memory. Customize `persona`, `human`, and [MemFS](/concepts/memfs/index.md) instead.

Set `systemPrompt` only when your application needs a deliberately different operating model — for example, a narrow pipeline worker that should not behave like a stateful assistant at all:

```
const agentId = await client.createAgent({
  model: "anthropic/claude-opus-4-8",
  memfs: false,
  systemPrompt:
    'You are a support-ticket triage classifier. For every message, respond with only a JSON object of the form {"category": string, "severity": "low" | "medium" | "high"}. Do not use tools. Do not add prose.',
});
```

If you find yourself writing a `systemPrompt` that describes who the agent is or how it should communicate, that belongs in the `persona` memory block instead.

`createAgent()` currently accepts custom system prompt strings, not preset names. `systemPrompt` and `disallowedTools` can only be set at agent creation; passing them to `createSession()` or `resumeSession()` throws.

## Manage existing agents

Use `client.agents` to work with agents outside a session:

```
const agents = await client.agents.list();
const agent = await client.agents.retrieve(agentId);
// → { id: "agent-3f97f111-…", name: "agent-d41dd2a0", created_at: "2026-08-11T07:01:45.195Z", … }
await client.agents.update(agentId, { description: "Updated description" });
await client.agents.delete(agentId);
```

Next: [send messages and process the response stream](/agent-sdk/messages/index.md).
