Skip to main content

Overview

Surfa provides two main APIs:
  1. Ingest API - Track events from your MCP server
  2. MCP Query API - Query analytics with natural language
Both APIs use API key authentication with Bearer tokens.

Ingest API

POST /api/v1/ingest/events

Track events from your MCP server or application. Endpoint:
POST https://surfa-web.vercel.app/api/v1/ingest/events
Headers:
Authorization: Bearer sk_live_your_key_here
Content-Type: application/json
Request Body:
{
  "session_id": "string (required)",
  "events": [
    {
      "kind": "string (required)",
      "subtype": "string (required)",
      // ... additional event fields
    }
  ],
  "execution_id": "string (optional)",
  "runtime": {
    "provider": "string (optional)",
    "model": "string (optional)",
    "mode": "string (optional)"
  }
}
Field Descriptions:
FieldTypeRequiredDescription
session_idstringUnique identifier for this session (e.g., live_session_abc123)
eventsarrayArray of event objects to track
execution_idstringExecution ID (returned from first request, reuse for subsequent requests)
runtimeobjectRuntime metadata (provider, model, mode)
Event Object:
FieldTypeRequiredDescription
kindstringEvent category: tool, session, runtime, error, etc.
subtypestringEvent type: call_started, call_completed, error, etc.
Additional fieldsanyAny custom fields (tool_name, status, latency_ms, etc.)
Response (Success):
{
  "ok": true,
  "execution_id": "exec_abc123xyz",
  "events_received": 1
}
Response (Error):
{
  "ok": false,
  "error": "Invalid API key"
}
Status Codes:
CodeMeaning
200Success - events tracked
401Unauthorized - invalid or missing API key
403Forbidden - API key revoked
422Validation error - invalid payload
429Rate limit exceeded
500Server error

Example Requests

Track a Tool Call

curl -X POST https://surfa-web.vercel.app/api/v1/ingest/events \
  -H "Authorization: Bearer sk_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "session_id": "live_session_abc123",
    "events": [
      {
        "kind": "tool",
        "subtype": "call_completed",
        "tool_name": "search",
        "status": "success",
        "latency_ms": 245
      }
    ]
  }'

Track Multiple Events

curl -X POST https://surfa-web.vercel.app/api/v1/ingest/events \
  -H "Authorization: Bearer sk_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "session_id": "live_session_abc123",
    "execution_id": "exec_xyz789",
    "events": [
      {
        "kind": "tool",
        "subtype": "call_started",
        "tool_name": "search",
        "query": "AI agents"
      },
      {
        "kind": "tool",
        "subtype": "call_completed",
        "tool_name": "search",
        "status": "success",
        "latency_ms": 245,
        "results_count": 10
      }
    ]
  }'

Track with Runtime Info

curl -X POST https://surfa-web.vercel.app/api/v1/ingest/events \
  -H "Authorization: Bearer sk_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "session_id": "live_session_abc123",
    "runtime": {
      "provider": "anthropic",
      "model": "claude-3-5-sonnet-20241022",
      "mode": "mcp"
    },
    "events": [
      {
        "kind": "session",
        "subtype": "started",
        "client_id": "claude_desktop_v1"
      }
    ]
  }'

Event Types

Tool Events

Track MCP tool calls:
{
  "kind": "tool",
  "subtype": "call_started" | "call_completed" | "call_failed",
  "tool_name": "string",
  "status": "success" | "error",
  "latency_ms": 123,
  "error_message": "string (if failed)"
}

Session Events

Track session lifecycle:
{
  "kind": "session",
  "subtype": "started" | "ended",
  "client_id": "string",
  "total_calls": 10
}

Error Events

Track errors:
{
  "kind": "error",
  "subtype": "error",
  "error_type": "string",
  "error_message": "string",
  "stack_trace": "string (optional)"
}

Custom Events

Track anything:
{
  "kind": "custom",
  "subtype": "your_event_type",
  "custom_field_1": "value",
  "custom_field_2": 123
}

MCP Query API

Used by the Surfa MCP Server to query analytics. You typically don’t call these directly - use the MCP server instead.

GET /api/v1/mcp/analytics/metrics

Get high-level analytics metrics. Headers:
Authorization: Bearer sk_live_your_key
Response:
{
  "ok": true,
  "data": {
    "totalSessions": 150,
    "successRate": 85,
    "avgExecutionTime": 245,
    "activeSessions": 12
  }
}

GET /api/v1/mcp/analytics/events

Query events with filters. Query Parameters:
ParameterTypeDescription
statusstringFilter by status: success, error
tool_namestringFilter by tool name
limitnumberMax results (default: 100)
Response:
{
  "ok": true,
  "data": {
    "events": [...],
    "total": 150
  }
}

GET /api/v1/mcp/analytics/sessions/:sessionId

Get all events for a specific session. Response:
{
  "ok": true,
  "data": {
    "session_id": "live_session_abc123",
    "status": "completed",
    "events": [...],
    "event_count": 10
  }
}

Rate Limits

Ingest API

The Ingest API has rate limiting to prevent abuse:
  • Limit: 1,000 events per minute per API key
  • Window: 60 seconds (sliding window)
  • Response: 429 Too Many Requests when exceeded
Rate Limit Response:
{
  "ok": false,
  "error": "Rate limit exceeded"
}
The SDK automatically retries with exponential backoff when rate limited.

MCP Query API

The MCP Query API currently has no rate limits. Use responsibly.

Error Handling

Authentication Errors (401, 403)

{
  "ok": false,
  "error": "Invalid API key"
}
Don’t retry - fix your API key.

Validation Errors (422)

{
  "ok": false,
  "error": "Missing required field: session_id"
}
Don’t retry - fix your payload.

Rate Limit Errors (429)

{
  "ok": false,
  "error": "Rate limit exceeded"
}
Retry with exponential backoff.

Server Errors (500, 502, 503, 504)

{
  "ok": false,
  "error": "Internal server error"
}
Retry with exponential backoff (max 3 attempts).

SDK vs Direct API

from surfa_ingest import SurfaClient

analytics = SurfaClient(
    ingest_key="sk_live_your_key",
    api_url="https://surfa-web.vercel.app"
)

analytics.track({
    "kind": "tool",
    "subtype": "call_completed",
    "tool_name": "search",
    "status": "success"
})
Benefits:
  • ✅ Automatic retries
  • ✅ Buffering and batching
  • ✅ Session management
  • ✅ Error handling
  • ✅ Runtime metadata

Direct API Calls

Use direct API calls if:
  • You’re not using Python
  • You need custom behavior
  • You’re building your own SDK
Example (JavaScript):
const response = await fetch('https://surfa-web.vercel.app/api/v1/ingest/events', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${apiKey}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    session_id: 'live_session_abc123',
    events: [{
      kind: 'tool',
      subtype: 'call_completed',
      tool_name: 'search',
      status: 'success'
    }]
  })
});

const data = await response.json();
console.log(data);

Next Steps