Skip to main content

Overview

Crewship provides real-time event streaming so you can:
  • Monitor crew execution as it happens
  • Build responsive UIs that update live
  • Debug issues by watching the event flow
  • Log events to external systems

Streaming via CLI

The simplest way to stream events:
crewship invoke --input '{"topic": "AI"}' --stream
Output updates in real-time:
▶ Run started: run_abc123
├─ [10:30:01] Starting crew execution
├─ [10:30:02] Researcher agent starting task
├─ [10:30:05] Tool: web_search("AI agents 2024")
├─ [10:30:12] Researcher agent completed task
├─ [10:30:13] Writer agent starting task
├─ [10:30:45] Writer agent completed task
├─ [10:30:46] Artifact: report.md
✅ Run completed in 45.2s

Streaming via API

Server-Sent Events (SSE)

Connect to the events endpoint with SSE:
curl -N \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: text/event-stream" \
  "https://api.crewship.dev/v1/runs/run_abc123/events"

JavaScript/TypeScript

const eventSource = new EventSource('https://api.crewship.dev/v1/runs/run_abc123/events', {
  headers: {
    Authorization: 'Bearer YOUR_API_KEY',
  },
})

eventSource.onmessage = (event) => {
  const data = JSON.parse(event.data)
  console.log(data.type, data.payload)
}

eventSource.onerror = (error) => {
  console.error('Stream error:', error)
  eventSource.close()
}

Python

import requests

response = requests.get(
    "https://api.crewship.dev/v1/runs/run_abc123/events",
    headers={
        "Authorization": "Bearer YOUR_API_KEY",
        "Accept": "text/event-stream"
    },
    stream=True
)

for line in response.iter_lines():
    if line:
        # Parse SSE format
        if line.startswith(b"data: "):
            data = json.loads(line[6:])
            print(data["type"], data["payload"])

Event Types

Run Lifecycle Events

EventDescriptionPayload
run.startedExecution began{ run_id, started_at }
run.completedSuccess{ run_id, duration_ms, result }
run.failedError{ run_id, error, stack_trace }
run.canceledManually stopped{ run_id, canceled_at }

Agent Events

EventDescriptionPayload
agent.startedAgent began task{ agent, task }
agent.completedAgent finished{ agent, task, output }
agent.errorAgent failed{ agent, error }

Tool Events

EventDescriptionPayload
tool.calledTool invoked{ tool, input }
tool.resultTool returned{ tool, output }
tool.errorTool failed{ tool, error }

Log Events

EventDescriptionPayload
logLog message{ level, message, timestamp }

Artifact Events

EventDescriptionPayload
artifactFile produced{ name, size, content_type }

Event Format

Each SSE event follows this format:
event: <event_type>
data: <json_payload>
id: <event_id>

Example:
event: agent.started
data: {"agent":"Researcher","task":"Research AI trends","timestamp":"2024-01-15T10:30:02Z"}
id: evt_001

event: tool.called
data: {"tool":"web_search","input":"AI agents 2024","timestamp":"2024-01-15T10:30:05Z"}
id: evt_002

event: log
data: {"level":"info","message":"Found 15 relevant results","timestamp":"2024-01-15T10:30:06Z"}
id: evt_003

Reconnection

SSE supports automatic reconnection. Use the Last-Event-ID header:
let lastEventId = localStorage.getItem('lastEventId')

const eventSource = new EventSource(`https://api.crewship.dev/v1/runs/${runId}/events`, {
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Last-Event-ID': lastEventId || '',
  },
})

eventSource.onmessage = (event) => {
  localStorage.setItem('lastEventId', event.lastEventId)
  // Process event...
}

Building a Live UI

Example React component:
function RunProgress({ runId }: { runId: string }) {
  const [events, setEvents] = useState<Event[]>([])
  const [status, setStatus] = useState<'running' | 'completed' | 'failed'>('running')

  useEffect(() => {
    const eventSource = new EventSource(`https://api.crewship.dev/v1/runs/${runId}/events`)

    eventSource.onmessage = (event) => {
      const data = JSON.parse(event.data)
      setEvents((prev) => [...prev, data])

      if (data.type === 'run.completed') {
        setStatus('completed')
        eventSource.close()
      } else if (data.type === 'run.failed') {
        setStatus('failed')
        eventSource.close()
      }
    }

    return () => eventSource.close()
  }, [runId])

  return (
    <div>
      <StatusBadge status={status} />
      <EventList events={events} />
    </div>
  )
}

Filtering Events

Request specific event types:
curl -N \
  -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.crewship.dev/v1/runs/run_abc123/events?types=agent.started,agent.completed,artifact"

Webhooks (Coming Soon)

Webhook delivery is on our roadmap. For now, use SSE for real-time events.