CLI Recipes

Bash recipes for common EpsimoAI API tasks. All examples use curl and jq. Requires a valid JWT — see Authentication for how to obtain one.


Prerequisites

# Install jq if not already available
# Ubuntu/Debian: sudo apt install jq
# macOS: brew install jq

# Base URL (set once)
export EPSIMO_URL="https://backend.epsimoagents.com"

Login and Save JWT

#!/bin/bash
# Login and persist the JWT for subsequent scripts

read -p "Email: " EMAIL
read -s -p "Password: " PASSWORD
echo

JWT=$(curl -s -X POST "$EPSIMO_URL/auth/login" \
  -H "Content-Type: application/json" \
  -d "{\"email\":\"$EMAIL\",\"password\":\"$PASSWORD\"}" \
  | jq -r .jwt_token)

if [ "$JWT" = "null" ] || [ -z "$JWT" ]; then
  echo "ERROR: Login failed. Check credentials."
  exit 1
fi

echo "$JWT" > ~/.epsimo-jwt
chmod 600 ~/.epsimo-jwt
echo "JWT saved to ~/.epsimo-jwt (expires in 30 days)"

Helper: Load JWT in Scripts

# Add to the top of any script
JWT=$(cat ~/.epsimo-jwt 2>/dev/null)
if [ -z "$JWT" ]; then
  echo "Not logged in. Run the login script first."
  exit 1
fi

List Projects

#!/bin/bash
JWT=$(cat ~/.epsimo-jwt)

echo "Your projects:"
curl -s "$EPSIMO_URL/projects/" \
  -H "Authorization: Bearer $JWT" \
  | jq -r '.[] | "\(.project_id)  \(.name)"'

Example output:

a1b2c3d4-...  My Main Project
e5f6a7b8-...  Client Demo

Switch Project

#!/bin/bash
# Switch to a specific project and save the project-scoped JWT
JWT=$(cat ~/.epsimo-jwt)
PROJECT_ID="$1"

if [ -z "$PROJECT_ID" ]; then
  echo "Usage: $0 <project-id>"
  echo "Run 'list-projects' to see available IDs."
  exit 1
fi

PROJECT_JWT=$(curl -s "$EPSIMO_URL/projects/$PROJECT_ID" \
  -H "Authorization: Bearer $JWT" \
  | jq -r .jwt_token)

if [ "$PROJECT_JWT" = "null" ] || [ -z "$PROJECT_JWT" ]; then
  echo "ERROR: Could not switch to project $PROJECT_ID"
  exit 1
fi

echo "$PROJECT_JWT" > ~/.epsimo-jwt
echo "Switched to project $PROJECT_ID. JWT updated."

List Threads

#!/bin/bash
JWT=$(cat ~/.epsimo-jwt)

curl -s "$EPSIMO_URL/threads" \
  -H "Authorization: Bearer $JWT" \
  | jq -r '.[] | "\(.thread_id)  \(.name // "unnamed")  \(.updated_at)"'

Create a Thread

#!/bin/bash
JWT=$(cat ~/.epsimo-jwt)
THREAD_NAME="${1:-New Thread}"

RESPONSE=$(curl -s -X POST "$EPSIMO_URL/threads" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d "{\"name\":\"$THREAD_NAME\"}")

THREAD_ID=$(echo "$RESPONSE" | jq -r .thread_id)
echo "Created thread: $THREAD_ID"
echo "$THREAD_ID"

Send a Message and Stream the Response

#!/bin/bash
# Usage: ./send-message.sh <assistant-id> <thread-id> "Your message"
JWT=$(cat ~/.epsimo-jwt)
ASSISTANT_ID="$1"
THREAD_ID="$2"
MESSAGE="$3"

if [ -z "$ASSISTANT_ID" ] || [ -z "$THREAD_ID" ] || [ -z "$MESSAGE" ]; then
  echo "Usage: $0 <assistant-id> <thread-id> \"message\""
  exit 1
fi

# Stream the response and parse SSE events
curl -s -N "$EPSIMO_URL/runs/stream" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d "{
    \"assistant_id\": \"$ASSISTANT_ID\",
    \"thread_id\": \"$THREAD_ID\",
    \"input\": [{\"role\": \"human\", \"content\": \"$MESSAGE\"}]
  }" | while IFS= read -r line; do
    # Skip empty lines and event type lines
    if [[ "$line" == data:* ]]; then
      DATA="${line#data: }"
      # Extract content from messages/partial events
      CONTENT=$(echo "$DATA" | jq -r '
        if type == "array" then
          .[-1].content // empty
        elif .content? then
          .content
        else
          empty
        end
      ' 2>/dev/null)
      if [ -n "$CONTENT" ]; then
        printf "\r%s" "$CONTENT"
      fi
    fi
  done
echo ""

Simpler Version (Collect Full Response)

#!/bin/bash
# Collects the full response without real-time streaming display
JWT=$(cat ~/.epsimo-jwt)
ASSISTANT_ID="$1"
THREAD_ID="$2"
MESSAGE="$3"

RESPONSE=$(curl -s -N "$EPSIMO_URL/runs/stream" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d "{
    \"assistant_id\": \"$ASSISTANT_ID\",
    \"thread_id\": \"$THREAD_ID\",
    \"input\": [{\"role\": \"human\", \"content\": \"$MESSAGE\"}]
  }" | grep "^data:" | tail -2 | head -1 | sed 's/^data: //')

echo "$RESPONSE" | jq -r '.[-1].content // .content // .'

List Assistants

#!/bin/bash
JWT=$(cat ~/.epsimo-jwt)

echo "Assistants in current project:"
curl -s "$EPSIMO_URL/assistants" \
  -H "Authorization: Bearer $JWT" \
  | jq -r '.[] | "\(.assistant_id)  \(.name)  [access: \(.config.configurable["type==agent/access_mode"] // "authenticated")]"'

Create an Assistant

#!/bin/bash
JWT=$(cat ~/.epsimo-jwt)
NAME="${1:-My New Assistant}"
SYSTEM_PROMPT="${2:-You are a helpful assistant.}"

RESPONSE=$(curl -s -X POST "$EPSIMO_URL/assistants" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d "{
    \"name\": \"$NAME\",
    \"config\": {
      \"configurable\": {
        \"type==agent/system_message\": \"$SYSTEM_PROMPT\",
        \"type==agent/access_mode\": \"authenticated\"
      }
    }
  }")

ASSISTANT_ID=$(echo "$RESPONSE" | jq -r .assistant_id)
echo "Created assistant: $ASSISTANT_ID ($NAME)"

Create a Public Assistant (with Token)

#!/bin/bash
JWT=$(cat ~/.epsimo-jwt)
NAME="${1:-Public Bot}"
TOKEN="pubkey_$(openssl rand -hex 16)"

RESPONSE=$(curl -s -X POST "$EPSIMO_URL/assistants" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d "{
    \"name\": \"$NAME\",
    \"config\": {
      \"configurable\": {
        \"type==agent/system_message\": \"You are a helpful assistant.\",
        \"type==agent/access_mode\": \"public_token\",
        \"type==agent/public_token\": \"$TOKEN\"
      }
    }
  }")

ASSISTANT_ID=$(echo "$RESPONSE" | jq -r .assistant_id)
echo "Created public assistant: $ASSISTANT_ID"
echo "Public token: $TOKEN"
echo ""
echo "Test it:"
echo "  curl $EPSIMO_URL/public/openai/v1/chat/completions \\"
echo "    -H 'Authorization: Bearer $TOKEN' \\"
echo "    -H 'Content-Type: application/json' \\"
echo "    -d '{\"model\":\"gpt-4o\",\"messages\":[{\"role\":\"user\",\"content\":\"Hello\"}]}'"

Full Interactive Chat Session

#!/bin/bash
# Interactive chat loop with an assistant
JWT=$(cat ~/.epsimo-jwt)
ASSISTANT_ID="$1"

if [ -z "$ASSISTANT_ID" ]; then
  echo "Usage: $0 <assistant-id>"
  exit 1
fi

# Create a new thread for this session
THREAD_ID=$(curl -s -X POST "$EPSIMO_URL/threads" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{"name":"CLI Chat Session"}' \
  | jq -r .thread_id)

echo "Chat session started (thread: $THREAD_ID)"
echo "Type 'quit' to exit."
echo "---"

while true; do
  read -p "You: " MESSAGE
  [ "$MESSAGE" = "quit" ] && break
  [ -z "$MESSAGE" ] && continue

  printf "Assistant: "
  curl -s -N "$EPSIMO_URL/runs/stream" \
    -H "Authorization: Bearer $JWT" \
    -H "Content-Type: application/json" \
    -d "{
      \"assistant_id\": \"$ASSISTANT_ID\",
      \"thread_id\": \"$THREAD_ID\",
      \"input\": [{\"role\": \"human\", \"content\": \"$MESSAGE\"}]
    }" | grep "^data:" | while IFS= read -r line; do
      DATA="${line#data: }"
      CONTENT=$(echo "$DATA" | jq -r '
        if type == "array" then .[-1].content // empty
        else empty end
      ' 2>/dev/null)
      [ -n "$CONTENT" ] && printf "\r%s" "$CONTENT"
    done
  echo ""
done

echo "Session ended."