Skip to main content

Endpoint

POST http://localhost:8080/api/v1/tasks

Description

Submits a new task to Shannon for execution. The task is queued immediately and processed asynchronously by the Temporal workflow engine.

Authentication

Required: Yes Include API key in header:
X-API-Key: sk_test_123456

Request

Headers

HeaderRequiredDescriptionExample
X-API-KeyYesAuthentication keysk_test_123456
Content-TypeYesMust be application/jsonapplication/json
Idempotency-KeyNoUnique key for idempotency550e8400-e29b-41d4-a716-446655440000
traceparentNoW3C trace context00-4bf92f...

Body Parameters

ParameterTypeRequiredDescription
querystringYesNatural language task description
session_idstringNoSession identifier for multi-turn conversations
contextobjectNoAdditional context data as key-value pairs
modestringNoWorkflow routing: simple, standard, complex, or supervisor
model_tierstringNoPreferred tier: small, medium, or large
model_overridestringNoSpecific model name (canonical; e.g., gpt-5, claude-sonnet-4-5-20250929)
provider_overridestringNoForce provider (e.g., openai, anthropic, google)

Request Body Schema

Example 1: General AI-powered execution
{
  "query": "Analyze August website traffic trends",              // REQUIRED: Task to execute
  "session_id": "analytics-session-123",                        // OPTIONAL: Session ID for multi-turn conversations (auto-generated if omitted)
  "mode": "supervisor",                                         // OPTIONAL: Workflow routing - "simple", "standard", "complex", or "supervisor" (default: auto-detect)
  "model_tier": "large",                                        // OPTIONAL: Model size - "small", "medium", or "large" (default: "small")
  "model_override": "gpt-5",                                   // OPTIONAL: Specific model (canonical id)
  "provider_override": "openai",                                // OPTIONAL: Force specific provider
  "context": {                                                  // OPTIONAL: Execution context object
    "role": "data_analytics",                                   // OPTIONAL: Role preset name (e.g., "analysis", "research", "writer")
    "system_prompt": "You are a data analyst...",              // OPTIONAL: Custom system prompt (overrides role preset)
    "prompt_params": {                                         // OPTIONAL: Arbitrary key-value pairs for prompts/tools/adapters
      "profile_id": "49598h6e",                                // EXAMPLE: Custom parameter (passed to tools/adapters)
      "aid": "fcb1cd29-9104-47b1-b914-31db6ba30c1a",          // EXAMPLE: Custom parameter (application ID)
      "current_date": "2025-10-31"                             // EXAMPLE: Custom parameter (current date)
    },
    "history_window_size": 75,                                 // OPTIONAL: Max conversation history messages (default: 50)
    "primers_count": 3,                                        // OPTIONAL: Number of early messages to keep (default: 5)
    "recents_count": 20,                                       // OPTIONAL: Number of recent messages to keep (default: 15)
    "compression_trigger_ratio": 0.75,                         // OPTIONAL: Trigger compression at 75% of window (default: 0.8)
    "compression_target_ratio": 0.375                          // OPTIONAL: Compress to 37.5% of window (default: 0.5)
  }
}
Example 2: Template-only execution (no AI)
{
  "query": "Generate weekly research briefing",                 // REQUIRED: Task description
  "session_id": "research-session-456",                        // OPTIONAL: Session ID
  "context": {                                                  // OPTIONAL: Context object
    "template": "research_summary",                            // OPTIONAL: Template name to use
    "template_version": "1.0.0",                               // OPTIONAL: Template version (default: latest)
    "disable_ai": true,                                        // OPTIONAL: Template-only mode, no AI fallback (default: false)
    "prompt_params": {                                         // OPTIONAL: Parameters for template rendering
      "week": "2025-W44"                                       // EXAMPLE: Custom parameter for template
    }
  }
}
Parameter Conflicts to Avoid:
  • Don’t use both template and template_name (they’re aliases - use template only)
  • Don’t combine disable_ai: true with model controls - Gateway returns 400 error when conflicts detected:
    • disable_ai: true + model_tier → 400
    • disable_ai: true + model_override → 400
    • disable_ai: true + provider_override → 400
  • Top-level parameters override context equivalents:
    • Top-level model_tier overrides context.model_tier
    • Top-level model_override overrides context.model_override
    • Top-level provider_override overrides context.provider_override

Context Parameters

Recognized keys in context:
  • role — role preset (e.g., analysis, research, writer)
  • system_prompt — overrides role prompt; supports ${var} from prompt_params
  • prompt_params — arbitrary parameters for prompts/tools/adapters
  • model_tier — fallback when top‑level not provided

Response

Success Response

Status: 200 OK Headers:
  • X-Workflow-ID: Temporal workflow identifier
  • X-Session-ID: Session identifier (auto-generated if not provided)
Body:
{
  "task_id": "string",
  "status": "string",
  "message": "string (optional)",
  "created_at": "timestamp"
}

Response Fields

FieldTypeDescription
task_idstringUnique task identifier (also workflow ID)
statusstringSubmission status (e.g., STATUS_CODE_OK)
messagestringOptional status message
created_attimestampTask creation time (ISO 8601)

Examples

Basic Task Submission

curl -X POST http://localhost:8080/api/v1/tasks \
  -H "X-API-Key: sk_test_123456" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "What is the capital of France?"
  }'
Response:
{
  "task_id": "task_01HQZX3Y9K8M2P4N5S7T9W2V",
  "status": "STATUS_CODE_OK",
  "message": "Task submitted successfully",
  "created_at": "2025-10-22T10:30:00Z"
}

Task with Session ID (Multi-Turn)

# First turn
curl -X POST http://localhost:8080/api/v1/tasks \
  -H "X-API-Key: sk_test_123456" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "What is Python?",
    "session_id": "user-123-chat"
  }'

# Second turn (references previous context)
curl -X POST http://localhost:8080/api/v1/tasks \
  -H "X-API-Key: sk_test_123456" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "What are its main advantages?",
    "session_id": "user-123-chat"
  }'

Task with Context

curl -X POST http://localhost:8080/api/v1/tasks \
  -H "X-API-Key: sk_test_123456" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "Summarize this user feedback",
    "context": {
      "user_id": "user_12345",
      "feedback_type": "bug_report",
      "severity": "high",
      "product": "mobile_app",
      "role": "analysis",
      "model_override": "gpt-5"
    }
  }'

Force Tier (Top‑Level)

curl -X POST http://localhost:8080/api/v1/tasks \
  -H "X-API-Key: sk_test_123456" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "Complex analysis",
    "model_tier": "large"
  }'

Template‑Only Execution

curl -X POST http://localhost:8080/api/v1/tasks \
  -H "X-API-Key: sk_test_123456" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "Weekly research briefing",
    "context": {"template": "research_summary", "template_version": "1.0.0", "disable_ai": true}
  }'

Supervisor Mode

curl -X POST http://localhost:8080/api/v1/tasks \
  -H "X-API-Key: sk_test_123456" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "Assess system reliability",
    "mode": "supervisor"
  }'

With Idempotency

# Generate idempotency key (use UUID)
IDEMPOTENCY_KEY=$(uuidgen)

curl -X POST http://localhost:8080/api/v1/tasks \
  -H "X-API-Key: sk_test_123456" \
  -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "Analyze sales data for Q4"
  }'

With Distributed Tracing

curl -X POST http://localhost:8080/api/v1/tasks \
  -H "X-API-Key: sk_test_123456" \
  -H "traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "Research latest AI trends"
  }'

Error Responses

400 Bad Request

Missing Query:
{
  "error": "Query is required"
}
Invalid JSON:
{
  "error": "Invalid request body: unexpected EOF"
}

401 Unauthorized

Missing API Key:
{
  "error": "Unauthorized"
}
Invalid API Key:
{
  "error": "Unauthorized"
}

429 Too Many Requests

{
  "error": "Rate limit exceeded"
}
Headers:
  • X-RateLimit-Limit: 100
  • X-RateLimit-Remaining: 0
  • X-RateLimit-Reset: 1609459200
  • Retry-After: 60

500 Internal Server Error

{
  "error": "Failed to submit task: database connection failed"
}

Code Examples

Python with httpx

import httpx

response = httpx.post(
    "http://localhost:8080/api/v1/tasks",
    headers={
        "X-API-Key": "sk_test_123456",
        "Content-Type": "application/json"
    },
    json={
        "query": "What is the capital of France?"
    }
)

if response.status_code == 200:
    data = response.json()
    print(f"Task ID: {data['task_id']}")
    print(f"Status: {data['status']}")
else:
    print(f"Error: {response.status_code}")
    print(response.json())

Python with requests

import requests

response = requests.post(
    "http://localhost:8080/api/v1/tasks",
    headers={
        "X-API-Key": "sk_test_123456"
    },
    json={
        "query": "Analyze customer sentiment",
        "context": {
            "source": "twitter",
            "date_range": "2025-10-01 to 2025-10-22"
        }
    }
)

task = response.json()
print(f"Task submitted: {task['task_id']}")

JavaScript/Node.js

const axios = require('axios');

async function submitTask(query) {
  try {
    const response = await axios.post(
      'http://localhost:8080/api/v1/tasks',
      {
        query: query
      },
      {
        headers: {
          'X-API-Key': 'sk_test_123456',
          'Content-Type': 'application/json'
        }
      }
    );

    console.log('Task ID:', response.data.task_id);
    console.log('Status:', response.data.status);

    return response.data;
  } catch (error) {
    console.error('Error:', error.response?.data || error.message);
    throw error;
  }
}

submitTask('What is quantum computing?');

cURL with Idempotency

#!/bin/bash

API_KEY="sk_test_123456"
IDEMPOTENCY_KEY=$(uuidgen)

# Submit task
RESPONSE=$(curl -s -X POST http://localhost:8080/api/v1/tasks \
  -H "X-API-Key: $API_KEY" \
  -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "Analyze Q4 revenue trends"
  }')

echo $RESPONSE | jq

TASK_ID=$(echo $RESPONSE | jq -r '.task_id')
echo "Track progress: http://localhost:8088/workflows/$TASK_ID"

Go

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
)

type TaskRequest struct {
    Query     string                 `json:"query"`
    SessionID string                 `json:"session_id,omitempty"`
    Context   map[string]interface{} `json:"context,omitempty"`
}

type TaskResponse struct {
    TaskID    string `json:"task_id"`
    Status    string `json:"status"`
    Message   string `json:"message,omitempty"`
}

func submitTask(query string) (*TaskResponse, error) {
    req := TaskRequest{
        Query: query,
    }

    body, _ := json.Marshal(req)

    httpReq, _ := http.NewRequest(
        "POST",
        "http://localhost:8080/api/v1/tasks",
        bytes.NewBuffer(body),
    )

    httpReq.Header.Set("X-API-Key", "sk_test_123456")
    httpReq.Header.Set("Content-Type", "application/json")

    client := &http.Client{}
    resp, err := client.Do(httpReq)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    var taskResp TaskResponse
    json.NewDecoder(resp.Body).Decode(&taskResp)

    return &taskResp, nil
}

func main() {
    task, err := submitTask("What is machine learning?")
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    fmt.Printf("Task ID: %s\n", task.TaskID)
    fmt.Printf("Status: %s\n", task.Status)
}

Implementation Details

Workflow Creation

When you submit a task:
  1. Gateway receives request → Validates authentication, rate limits
  2. Generates session ID → If not provided, auto-generates UUID
  3. Calls Orchestrator gRPCSubmitTask(metadata, query, context)
  4. Orchestrator starts Temporal workflow → Durable execution
  5. Response returned → Task ID, initial status
  6. Task executes asynchronously → Independent of HTTP connection

Idempotency Behavior

Idempotency keys allow safe retries of task submissions without creating duplicate tasks. How it works:
  1. First request with an Idempotency-Key:
    • Shannon creates the task
    • Caches the response in Redis with 24-hour TTL
    • Returns task ID and status
  2. Duplicate requests (same Idempotency-Key):
    • Shannon detects the cached response
    • Returns the same task ID without creating a new task
    • Response is identical to the first request
  3. After 24 hours:
    • Cache expires
    • New request with same key creates a new task
Cache Details:
  • Storage: Redis
  • TTL: 24 hours (86400 seconds)
  • Key format: idempotency:<16-char-hash> (SHA-256 of the idempotency key plus user ID, path, and request body)
  • Scope: Per authenticated user (user ID is part of the hash; when auth is disabled the hash is based on the header, path, and body)
  • Cached responses: Only 2xx responses are stored; cached hits include X-Idempotency-Cached: true and X-Idempotency-Key: <your-key>
Body Behavior: If the request body changes, the cache key changes too, so the gateway treats it as a brand-new request. Duplicate detection only triggers when the header, user, path, and body all match. Best Practice: Generate a unique key per unique request body. Example:
import uuid
import httpx

# Generate unique key
idempotency_key = str(uuid.uuid4())

# First request - creates task
response1 = httpx.post(
    "http://localhost:8080/api/v1/tasks",
    headers={"X-API-Key": "sk_test_123456", "Idempotency-Key": idempotency_key},
    json={"query": "Analyze Q4 sales"}
)
task_id_1 = response1.json()["task_id"]

# Retry with same key - returns same task ID
response2 = httpx.post(
    "http://localhost:8080/api/v1/tasks",
    headers={"X-API-Key": "sk_test_123456", "Idempotency-Key": idempotency_key},
    json={"query": "Analyze Q4 sales"}
)
task_id_2 = response2.json()["task_id"]

assert task_id_1 == task_id_2  # Same task, no duplicate
When to use:
  • Network retry logic (avoid duplicate tasks on timeout)
  • Webhook deliveries (handle duplicate webhook calls)
  • Critical operations (payments, data writes)
  • Background job queues (prevent duplicate scheduling)

Session Management

  • No session_id: Auto-generates UUID, fresh context
  • With session_id: Loads previous conversation history from Redis
  • Session persistence: 30 days default TTL
  • Multi-turn conversations: All tasks with same session_id share context

Context Object

The context object is stored as metadata and passed to:
  • Agent execution environment
  • Tool invocations (can access via ctx.get("key"))
  • Session memory (for reference in future turns)
Example use cases:
  • User preferences: {"language": "spanish", "format": "markdown"}
  • Business context: {"company_id": "acme", "department": "sales"}
  • Constraints: {"max_length": 500, "tone": "formal"}

Best Practices

1. Always Use Idempotency Keys for Critical Tasks

import uuid

idempotency_key = str(uuid.uuid4())

response = httpx.post(
    "http://localhost:8080/api/v1/tasks",
    headers={
        "X-API-Key": "sk_test_123456",
        "Idempotency-Key": idempotency_key
    },
    json={"query": "Process payment for order #12345"}
)

2. Use Sessions for Conversations

session_id = "user-456-chat"

# Turn 1
httpx.post(..., json={
    "query": "Load sales data for Q4",
    "session_id": session_id
})

# Turn 2 (references Q4 data from Turn 1)
httpx.post(..., json={
    "query": "Compare it to Q3",
    "session_id": session_id
})

3. Provide Rich Context

httpx.post(..., json={
    "query": "Analyze this customer feedback",
    "context": {
        "customer_id": "cust_789",
        "subscription_tier": "enterprise",
        "account_age_days": 365,
        "previous_tickets": 3,
        "sentiment_history": ["positive", "neutral", "negative"]
    }
})

4. Handle Errors Gracefully

try:
    response = httpx.post(..., timeout=30.0)
    response.raise_for_status()
    task = response.json()
except httpx.TimeoutException:
    print("Request timed out")
except httpx.HTTPStatusError as e:
    if e.response.status_code == 429:
        retry_after = int(e.response.headers.get("Retry-After", 60))
        time.sleep(retry_after)
        # Retry...
    else:
        print(f"Error: {e.response.json()}")

5. Store Task IDs for Tracking

response = httpx.post(...)
task_id = response.json()["task_id"]
workflow_id = response.headers["X-Workflow-ID"]

# Save to database
db.tasks.insert({
    "task_id": task_id,
    "workflow_id": workflow_id,
    "user_id": "user_123",
    "query": "...",
    "created_at": datetime.now()
})

# Later: check status
status = httpx.get(f"http://localhost:8080/api/v1/tasks/{task_id}")

Submit + Stream in One Call

Need real-time updates? Use POST /api/v1/tasks/stream instead to submit a task and get a stream URL in one call. Perfect for frontend applications that need immediate progress updates.See Unified Submit + Stream for examples.