Letta REST API vs Letta Code SDK: 8 Key Differences in the Claude-Subconscious Plugin

The Letta REST API requires manual HTTP handling and polling for responses, while the Letta Code SDK provides a high-level session abstraction with real-time streaming, automatic authentication, and configurable client-side tool access.

The letta-ai/claude-subconscious repository implements dual integration patterns for connecting Claude Code with Letta agents. When deciding between the Letta REST API and Letta Code SDK, developers must consider differences in abstraction level, streaming capabilities, and tool permissions that fundamentally change how the plugin synchronizes conversation state.

Architectural Overview: Low-Level Control vs. Session Abstraction

Raw HTTP Implementation with the REST API

The REST API approach implements direct HTTP communication using Node’s native fetch to interact with Letta endpoints such as /conversations/ and /agents/. In scripts/conversation_utils.ts, the createConversation() function constructs URLs via buildLettaApiUrl() and manually assembles request headers including the Authorization: Bearer <API-KEY> credential.

This method requires the plugin to handle XML payload formatting, JSON response parsing, and HTTP error status codes without abstraction layers. The send_messages_to_letta.ts script writes temporary JSON files to disk and tracks message indices manually when preparing the <claude_code_session_update> payload for transmission.

High-Level Session Management with the Code SDK

The Code SDK approach leverages the optional @letta-ai/letta-code-sdk package to create a persistent session object. In scripts/send_worker_sdk.ts, the worker dynamically imports the SDK and invokes resumeSession() with a conversation ID and configuration options. The SDK encapsulates authentication by reading the LETTA_API_KEY environment variable internally, eliminating manual header management.

The session object exposes session.send() for transmitting messages and session.stream() for receiving real-time assistant responses, abstracting the underlying HTTP transport and payload serialization.

Key Implementation Differences

Authentication Patterns

REST API: Each request must explicitly include the bearer token in the request headers. The createConversation() function in scripts/conversation_utils.ts (lines 20-28) demonstrates this pattern by passing the apiKey parameter into the Authorization header for every fetch call.

Code SDK: Authentication is transparent. The SDK reads LETTA_API_KEY from environment variables during session initialization, as implemented in scripts/send_worker_sdk.ts (lines 40-46). Callers do not manage headers or credential injection.

Message Transport and Payload Handling

REST API: The plugin bears full responsibility for payload construction. In send_messages_to_letta.ts, the code manually formats XML envelopes containing the transcript updates, writes these to temporary files, and later parses the conversation history JSON to extract responses.

Code SDK: Message formatting happens automatically. The worker passes the raw XML string to session.send(), which handles serialization and transmission. The SDK manages conversation state and memory synchronization without requiring temporary file intermediates.

Response Retrieval: Polling vs. Streaming

REST API: Responses are retrieved via periodic polling. The pretool_sync.ts and sync_letta_memory.ts scripts implement fetchNewMessages() to query the conversation endpoint later, checking for new assistant messages to inject into CLAUDE.md. This creates latency between message generation and availability.

Code SDK: Responses stream in real-time. The send_worker_sdk.ts implementation uses for await (const msg of session.stream()) (lines 73-93) to process assistant chunks as they arrive, enabling immediate feedback without polling loops.

Tool Access and Capability Differences

Client-Side Tool Permissions

The REST API integration cannot access Letta’s client-side tools such as Read, Grep, Glob, or web_search. The plugin is restricted to text-only send/receive operations when using HTTP endpoints directly.

The Code SDK exposes configurable tool permissions through the LETTA_SDK_TOOLS environment variable. In scripts/send_worker_sdk.ts (lines 69-87), the worker configures allowedTools and disallowedTools arrays based on the requested mode:

  • read-only: Enables Read, Grep, Glob, web_search, and fetch_webpage
  • full: Allows all client-side capabilities including Bash, Edit, and Write
  • off: Disables all tools for text-only interaction

Dependency and Import Strategy

REST API: Zero external dependencies required. The implementation relies solely on Node.js built-in fetch, making it suitable for lightweight deployments where minimizing package size is critical.

Code SDK: Requires the optional @letta-ai/letta-code-sdk npm package. The plugin safeguards against missing dependencies by using dynamic imports in send_worker_sdk.ts, allowing the plugin to run in REST-only mode if the SDK is not installed.

Code Examples: REST API vs. SDK

Creating Conversations via REST

The following implementation from scripts/conversation_utils.ts demonstrates direct API communication for conversation initialization:

import { buildLettaApiUrl } from './letta_api_url.js';

export async function createConversation(
  apiKey: string,
  agentId: string,
  log: LogFn = noopLog,
): Promise<string> {
  const url = buildLettaApiUrl('/conversations/', { agent_id: agentId });

  log(`Creating new conversation for agent ${agentId}`);

  const response = await fetch(url, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${apiKey}`,
      'Content-Type': 'application/json',
    },
  });

  if (!response.ok) {
    const err = await response.text();
    throw new Error(`Failed to create conversation: ${response.status} ${err}`);
  }

  const conversation: Conversation = await response.json();
  log(`Created conversation: ${conversation.id}`);
  return conversation.id;
}

This function corresponds to lines 20-28 in the source file and represents the typical pattern for REST-based agent configuration in agent_config.ts.

Streaming Messages via SDK

The background worker in scripts/send_worker_sdk.ts implements the SDK approach for live transcript updates:

import { resumeSession } from '@letta-ai/letta-code-sdk';

async function sendViaSdk(payload: SdkPayload): Promise<boolean> {
  const sessionOptions: Record<string, unknown> = {
    disallowedTools: ['AskUserQuestion', 'EnterPlanMode', 'ExitPlanMode'],
    permissionMode: 'bypassPermissions',
    cwd: payload.cwd,
    skillSources: [],
    systemInfoReminder: false,
    sleeptime: { trigger: 'off' },
  };

  if (payload.sdkToolsMode === 'read-only') {
    sessionOptions.allowedTools = ['Read', 'Grep', 'Glob', 'web_search', 'fetch_webpage'];
  } else if (payload.sdkToolsMode === 'off') {
    sessionOptions.disallowedTools = [
      ...sessionOptions.disallowedTools,
      'Read',
      'Grep',
      'Glob',
      'Bash',
      'Edit',
      'Write',
      'Task',
    ];
  }

  const session = resumeSession(payload.conversationId, sessionOptions);

  try {
    await session.send(payload.message);

    for await (const msg of session.stream()) {
      if (msg.type === 'assistant') {
        // Process streaming assistant chunks
      } else if (msg.type === 'error') {
        // Handle SDK error objects
        return false;
      }
    }
    return true;
  } finally {
    session.close();
  }
}

This implementation (lines 40-78) shows how the SDK handles authentication, tool configuration, and real-time streaming without manual HTTP intervention.

When to Use Each Integration Method

Choose the REST API when:

  • Implementing background synchronization tasks like sync_letta_memory.ts or pretool_sync.ts
  • Minimizing external dependencies is required
  • Performing administrative operations such as creating conversations or updating agent configurations via agent_config.ts

Choose the Code SDK when:

  • Sending live transcript updates from send_messages_to_letta.ts to the Letta agent
  • Requiring real-time streaming responses instead of polling
  • Enabling client-side tool access (Read, Grep, Bash) for the agent
  • Simplifying authentication management in worker threads

Summary

  • Communication Style: The REST API uses manual HTTP requests with fetch, while the SDK provides a resumeSession() abstraction that hides network complexity.
  • Authentication: REST requires explicit Authorization headers per request; the SDK reads LETTA_API_KEY automatically from environment variables.
  • Response Handling: REST relies on periodic polling via fetchNewMessages() in sync scripts; the SDK streams responses in real-time through session.stream().
  • Tool Access: REST supports text-only interaction; the SDK enables configurable client-side tools via LETTA_SDK_TOOLS environment settings.
  • Dependencies: REST requires no external packages; the SDK uses optional dynamic imports of @letta-ai/letta-code-sdk.
  • Use Cases: REST is preferred for memory synchronization and conversation management; the SDK is optimized for live messaging with tool capabilities.

Frequently Asked Questions

Can I use both the REST API and Code SDK in the same plugin instance?

Yes. The letta-ai/claude-subconscious plugin employs both methods simultaneously according to their strengths. The REST API handles background synchronization tasks in sync_letta_memory.ts and pretool_sync.ts, while the Code SDK manages live transcript streaming via send_worker_sdk.ts. The dynamic import strategy ensures the plugin functions even if the SDK package is not installed.

Do I need to install the Letta Code SDK to use the plugin?

No. The @letta-ai/letta-code-sdk package is an optional dependency. The plugin defaults to REST API functionality for all operations if the SDK is absent. The send_worker_sdk.ts script uses dynamic imports to load the SDK only when available, falling back to REST-based alternatives or graceful degradation.

Which integration method supports real-time streaming of agent responses?

Only the Letta Code SDK supports real-time streaming. The session.stream() method in scripts/send_worker_sdk.ts (lines 73-93) yields assistant messages as they are generated. The REST API requires polling mechanisms like fetchNewMessages() to check for new content, introducing delays between message creation and retrieval.

How do I enable client-side tools like Read and Grep for the Letta agent?

Client-side tools require the Code SDK and the LETTA_SDK_TOOLS environment variable. Set this variable to read-only to enable safe tools (Read, Grep, Glob, web_search), full to allow all capabilities including Bash and Edit, or off to disable tools entirely. The REST API cannot access these client-side capabilities.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →