Docs API Reference
REST API v1

API Reference

Programmatic access to every Stryda surface — workflows, agents, approvals, the MCP control plane, and the audit ledger. All endpoints accept and return JSON. Base URL: https://stryda.ai/api/v1.

Authentication

REST endpoints use an API key passed in the x-api-key header. Generate keys in Settings → API Keys. MCP endpoints use a bearer token in the Authorization header — see the MCP section below.

Authenticated request
curl https://stryda.ai/api/v1/workflows \
  -H "x-api-key: sk_live_xxxxxxxxxxxxxxxx"

Rate limits

API endpoints: 60 requests per minute per key. Chatbot embed endpoints: 30 per minute per IP. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers.

GET /api/auth/me

Get the currently authenticated user's profile and plan details.

Example request
curl https://stryda.ai/api/auth/me \
  -H "Authorization: Bearer YOUR_FIREBASE_TOKEN"
Response
{
  "id": "user_abc123",
  "email": "[email protected]",
  "name": "Jane Smith",
  "plan": "free",
  "has_ai_access": true
}
GET /api/auth/validate-key

Validate an API key and return the associated user.

Example request
curl https://stryda.ai/api/auth/validate-key \
  -H "x-api-key: sk_live_xxxxxxxxxxxxxxxx"
Response
{
  "valid": true,
  "user_id": "user_abc123",
  "scopes": ["workflows", "chatbots", "agents", "tables", "forms", "memory"]
}

MCP control plane

A single MCP server at /api/mcp exposes every tool, namespaced by surface. Every tools/call goes through the same pipeline — validate → policy → approval → adapter → audit — with no bypass. Clients connect with a bearer token generated under Settings → MCP Clients.

Transport

Streamable HTTP with JSON-RPC 2.0 is the default. SSE is supported at /api/mcp/sse for legacy clients.

POST /api/mcp

The MCP JSON-RPC endpoint. Call tools/list to discover the catalog, tools/call to invoke a tool.

Example request
curl -X POST https://stryda.ai/api/mcp \
  -H "Authorization: Bearer whsec_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "comms.send_slack_message",
      "arguments": { "channel": "#general", "text": "hello" }
    }
  }'
Request body
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "comms.send_slack_message",
    "arguments": { "channel": "#general", "text": "hello" }
  }
}
Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      { "type": "text", "text": "{\"ok\":true,\"ts\":\"1718212...\",\"_cost_usd\":0}" }
    ]
  }
}

When a tool is gated (for example payments.create_refund, which registers with requires_approval=True), the pipeline forces an escalation regardless of scope:

Escalation response (approval required)
{
  "ok": false,
  "status": "action_required",
  "reason": "Tool requires explicit approval.",
  "escalation_id": "esc_abc123",
  "tool": "payments.create_refund"
}

Catalog preview (without MCP transport)

For the onboarding UI and programmatic inspection, a plain REST endpoint returns the tool catalog:

GET /api/mcp/tools

Returns the registered tool catalog — name, namespace, input schema, risk level, and whether approval is required.

Example request
curl https://stryda.ai/api/mcp/tools \
  -H "x-api-key: YOUR_KEY"
Response
{
  "tools": [
    {
      "name": "comms.send_slack_message",
      "namespace": "comms",
      "description": "Post a message to a Slack channel or DM.",
      "input_schema": { "type": "object", "properties": { "channel": { "type": "string" }, "text": { "type": "string" } }, "required": ["channel", "text"] },
      "requires_approval": false,
      "risk": "low"
    },
    {
      "name": "payments.create_refund",
      "namespace": "payments",
      "description": "Issue a Stripe refund.",
      "requires_approval": true,
      "risk": "high"
    }
  ]
}

Client registration

POST /api/mcp/clients

Register an MCP client (Claude Desktop, Cursor, or custom). Returns a bearer token shown once.

Request body
{
  "name": "Zain's laptop — Claude Desktop",
  "kind": "claude_desktop"
}
Response
{
  "client_id": "cli_abc123",
  "kind": "claude_desktop",
  "bearer": "whsec_xxxxxxxxxxxxxxxx",
  "url": "https://mcp.stryda.ai/api/mcp",
  "created_at": "2026-04-19T12:00:00Z"
}
POST /api/mcp/clients/{client_id}/rotate

Rotate the bearer token. The old token stops working immediately.

Response
{
  "client_id": "cli_abc123",
  "bearer": "whsec_yyyyyyyyyyyyyyyy",
  "rotated_at": "2026-04-19T12:30:00Z"
}
POST /api/mcp/clients/{client_id}/revoke

Revoke a client. Sets status to disconnected and nulls the bearer.

Response
{
  "client_id": "cli_abc123",
  "status": "disconnected",
  "revoked_at": "2026-04-19T12:45:00Z"
}

Available tools

ToolPurpose
comms.send_slack_messagePost a message to a Slack channel or DM.
comms.send_emailSend an email via the connected Gmail account.
crm.create_contactCreate a HubSpot contact.
payments.create_refundIssue a Stripe refund. Always requires human approval.
workflow.zapier_actionTrigger a Zap on a connected Zapier account.
workflow.n8n_triggerTrigger an n8n workflow.
workflow.make_scenarioRun a Make scenario.

Want the full input schemas? GET /api/mcp/tools returns them. Want to add a new tool? See Platforms and the MCP architecture doc in the repo.

Workflows

GET /api/v1/workflows

List all workflows for the authenticated user.

Example request
curl https://stryda.ai/api/v1/workflows \
  -H "x-api-key: YOUR_KEY"
Response
[
  {
    "id": "wf_abc123",
    "name": "Customer Support Triage",
    "status": "published",
    "node_count": 5,
    "created_at": "2026-01-10T09:00:00Z",
    "updated_at": "2026-01-14T15:30:00Z"
  }
]
POST /api/v1/workflows

Create a new workflow.

Request body
{
  "name": "Lead Scoring Pipeline",
  "description": "Score and route inbound leads"
}
Response
{
  "id": "wf_def456",
  "name": "Lead Scoring Pipeline",
  "status": "draft",
  "created_at": "2026-01-15T12:00:00Z"
}
PATCH /api/v1/workflows/{workflow_id}

Update a workflow's name, description, or status.

Request body
{
  "name": "Lead Scoring v2",
  "status": "published"
}
Response
{
  "id": "wf_def456",
  "name": "Lead Scoring v2",
  "status": "published",
  "updated_at": "2026-01-15T14:00:00Z"
}
DELETE /api/v1/workflows/{workflow_id}

Delete a workflow and all its execution history.

Example request
curl -X DELETE https://stryda.ai/api/v1/workflows/wf_def456 \
  -H "x-api-key: YOUR_KEY"
Response
{ "deleted": true }
POST /api/v1/workflows/{workflow_id}/run

Trigger a workflow run. Optional input_data is passed to the Trigger node.

Example request
curl -X POST https://stryda.ai/api/v1/workflows/wf_abc123/run \
  -H "x-api-key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"input_data": {"prompt": "Analyze this email"}}'
Request body
{
  "input_data": {
    "prompt": "Analyze this customer email and suggest a response"
  }
}
Response
{
  "run_id": "run_789",
  "workflow_id": "wf_abc123",
  "status": "running",
  "started_at": "2026-01-15T12:00:00Z"
}

Agents

GET /api/v1/agents

List all agents.

Response
[
  {
    "id": "agent_abc123",
    "name": "Research Agent",
    "model": "claude-opus-4-7",
    "tools": ["web_search", "file_read", "slack_post"],
    "status": "active",
    "created_at": "2026-01-10T09:00:00Z"
  }
]
POST /api/v1/agents

Create a new agent with tool access and scope.

Request body
{
  "name": "Data Analyst",
  "model": "claude-sonnet-4-6",
  "system_prompt": "You are a data analyst with access to company databases.",
  "tools": ["sql_query", "chart_create", "slack_post"],
  "max_iterations": 10,
  "budget_per_run": 0.50
}
Response
{
  "id": "agent_def456",
  "name": "Data Analyst",
  "status": "active",
  "created_at": "2026-01-15T12:00:00Z"
}
POST /api/v1/agents/{agent_id}/run

Trigger an agent run. The agent executes autonomously through the MCP pipeline using its configured tools.

Example request
curl -X POST https://stryda.ai/api/v1/agents/agent_abc123/run \
  -H "x-api-key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"input_data": {"task": "Analyze Q4 revenue trends"}}'
Request body
{
  "input_data": {
    "task": "Analyze Q4 revenue trends and post a summary to #analytics"
  }
}
Response
{
  "run_id": "run_012",
  "agent_id": "agent_abc123",
  "status": "running",
  "started_at": "2026-01-15T12:00:00Z"
}
DELETE /api/v1/agents/{agent_id}

Delete an agent.

Response
{ "deleted": true }

Chatbots

GET /api/v1/chatbots

List all chatbots.

Response
[
  {
    "id": "cb_abc123",
    "name": "Support Bot",
    "model": "claude-sonnet-4-6",
    "embed_id": "emb_xyz",
    "status": "deployed",
    "created_at": "2026-01-10T09:00:00Z"
  }
]
POST /api/v1/chatbots

Create a new chatbot.

Request body
{
  "name": "Sales Assistant",
  "model": "gpt-4.1",
  "system_prompt": "You are a helpful sales assistant.",
  "knowledge_base": ["kb_doc1", "kb_doc2"]
}
Response
{
  "id": "cb_def456",
  "name": "Sales Assistant",
  "embed_id": "emb_abc",
  "status": "draft",
  "created_at": "2026-01-15T12:00:00Z"
}
POST /api/embed/{embed_id}/chat Public

Send a message to a deployed chatbot. No API key required for public chatbots.

Example request
curl -X POST https://stryda.ai/api/embed/emb_xyz/chat \
  -H "Content-Type: application/json" \
  -d '{"message": "How do I reset my password?", "session_id": "sess_user123"}'
Request body
{
  "message": "How do I reset my password?",
  "session_id": "sess_user123"
}
Response
{
  "response": "To reset your password, go to Settings > Security...",
  "session_id": "sess_user123",
  "tokens": 245,
  "cost": 0.0008
}

Embed via script tag

Paste into any HTML page to render the chat widget:

embed.js
<script
  src="https://stryda.ai/embed.js"
  data-chatbot-id="YOUR_EMBED_ID"
  async
></script>

Runs

GET /api/v1/runs

List execution runs across all workflows and agents. Supports filtering by status and workflow_id.

Example request
curl "https://stryda.ai/api/v1/runs?status=completed&limit=10" \
  -H "x-api-key: YOUR_KEY"
Response
[
  {
    "run_id": "run_789",
    "workflow_id": "wf_abc123",
    "status": "completed",
    "total_cost": 0.0847,
    "started_at": "2026-01-15T12:00:00Z",
    "completed_at": "2026-01-15T12:00:12Z"
  }
]
GET /api/v1/workflows/{workflow_id}/runs/{run_id}

Get detailed information about a specific run, including per-node cost breakdown and every tool call in the MCP audit log.

Response
{
  "run_id": "run_789",
  "workflow_id": "wf_abc123",
  "status": "completed",
  "total_cost": 0.0847,
  "total_tokens": 15650,
  "nodes": [
    { "node_id": "classify", "model": "gpt-4.1-mini", "status": "completed", "cost": 0.0006 },
    { "node_id": "analyze", "model": "claude-sonnet-4-6", "status": "completed", "cost": 0.0606 },
    { "node_id": "output", "status": "completed", "cost": 0 }
  ],
  "mcp_calls": [
    { "tool": "comms.send_slack_message", "decision": "authorized", "cost": 0, "latency_ms": 184 }
  ],
  "output": { "summary": "Customer reported login issue...", "priority": "high" },
  "started_at": "2026-01-15T12:00:00Z",
  "completed_at": "2026-01-15T12:00:12Z"
}

Approvals

POST /api/v1/approvals/{id}/approve

Approve a pending HITL gate. The same tool call payload re-runs with the approval on file.

Example request
curl -X POST https://stryda.ai/api/v1/approvals/appr_abc123/approve \
  -H "x-api-key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"comment": "Approved"}'
Request body
{
  "comment": "Looks good, approved for release.",
  "edited_output": null
}
Response
{
  "id": "appr_abc123",
  "action": "approved",
  "reviewer": "user_abc123",
  "comment": "Looks good, approved for release.",
  "approved_at": "2026-01-15T14:32:00Z"
}
POST /api/v1/approvals/{id}/reject

Reject a pending HITL gate. Execution stops or follows the configured fallback branch.

Request body
{
  "comment": "Output contains inaccurate pricing. Please revise.",
  "reason": "factual_error"
}
Response
{
  "id": "appr_abc123",
  "action": "rejected",
  "reviewer": "user_abc123",
  "comment": "Output contains inaccurate pricing. Please revise.",
  "rejected_at": "2026-01-15T14:32:00Z"
}

Budget

GET /api/v1/budget

Get overall budget status and spending summary.

Response
{
  "monthly_cap": 200.00,
  "monthly_spent": 47.23,
  "monthly_remaining": 152.77,
  "daily_spent": 3.12,
  "alerts": [
    { "threshold_percent": 80, "channels": ["slack", "email"], "triggered": false }
  ]
}
PATCH /api/v1/budget

Update budget caps and alert thresholds.

Request body
{
  "monthly_cap": 300.00,
  "per_run_cap": 1.00,
  "alerts": [
    { "threshold_percent": 75, "channels": ["slack"] },
    { "threshold_percent": 95, "channels": ["slack", "email"] }
  ]
}
Response
{
  "monthly_cap": 300.00,
  "per_run_cap": 1.00,
  "updated_at": "2026-01-15T14:00:00Z"
}

Tables

GET /api/v1/tables

List all tables.

Response
[
  {
    "id": "tbl_abc123",
    "name": "Customer Leads",
    "columns": ["name", "email", "score", "status"],
    "row_count": 1247,
    "created_at": "2026-01-10T09:00:00Z"
  }
]
POST /api/v1/tables

Create a new table with column definitions.

Request body
{
  "name": "Product Feedback",
  "columns": [
    { "name": "customer", "type": "text" },
    { "name": "rating", "type": "number" },
    { "name": "feedback", "type": "text" },
    { "name": "category", "type": "select", "options": ["bug", "feature", "praise"] }
  ]
}
Response
{
  "id": "tbl_def456",
  "name": "Product Feedback",
  "columns": ["customer", "rating", "feedback", "category"],
  "row_count": 0,
  "created_at": "2026-01-15T12:00:00Z"
}
POST /api/v1/tables/{id}/rows

Add a row to a table.

Request body
{
  "data": {
    "customer": "Jane Smith",
    "rating": 5,
    "feedback": "The workflow builder is incredibly intuitive",
    "category": "praise"
  }
}
Response
{
  "id": "row_789",
  "table_id": "tbl_def456",
  "data": {
    "customer": "Jane Smith",
    "rating": 5,
    "feedback": "The workflow builder is incredibly intuitive",
    "category": "praise"
  },
  "created_at": "2026-01-15T12:30:00Z"
}
GET /api/v1/tables/{id}/rows

List rows in a table. Pagination via limit and offset query parameters.

Example request
curl "https://stryda.ai/api/v1/tables/tbl_abc123/rows?limit=50&offset=0" \
  -H "x-api-key: YOUR_KEY"
Response
{
  "rows": [
    { "id": "row_001", "data": { "name": "Acme Corp", "score": 85 } },
    { "id": "row_002", "data": { "name": "Globex", "score": 72 } }
  ],
  "total": 1247,
  "limit": 50,
  "offset": 0
}
DELETE /api/v1/tables/{id}

Delete a table and all its rows.

Response
{ "deleted": true }

Forms

GET /api/v1/forms

List all forms.

Response
[
  {
    "id": "frm_abc123",
    "name": "Contact Form",
    "slug": "contact",
    "submission_count": 342,
    "linked_table": "tbl_abc123",
    "status": "published",
    "created_at": "2026-01-10T09:00:00Z"
  }
]
POST /api/v1/forms

Create a new form.

Request body
{
  "name": "Bug Report",
  "slug": "bug-report",
  "fields": [
    { "name": "title", "type": "text", "required": true },
    { "name": "description", "type": "textarea", "required": true },
    { "name": "severity", "type": "select", "options": ["low", "medium", "high", "critical"] }
  ],
  "linked_table": "tbl_def456",
  "trigger_workflow": "wf_abc123"
}
Response
{
  "id": "frm_def456",
  "name": "Bug Report",
  "slug": "bug-report",
  "public_url": "https://stryda.ai/f/bug-report",
  "created_at": "2026-01-15T12:00:00Z"
}
POST /api/v1/forms/{id}/submit Public

Submit a form entry. Triggers any linked workflow and populates the linked table.

Request body
{
  "data": {
    "title": "Dashboard loading slowly",
    "description": "The analytics dashboard takes 15 seconds to load",
    "severity": "medium"
  }
}
Response
{
  "id": "sub_789",
  "form_id": "frm_def456",
  "status": "received",
  "workflow_run_id": "run_012",
  "created_at": "2026-01-15T12:30:00Z"
}

Memory

GET /api/v1/memory

List all memory entries.

Example request
curl https://stryda.ai/api/v1/memory \
  -H "x-api-key: YOUR_KEY"
Response
[
  {
    "id": "mem_abc123",
    "content": "User prefers concise responses",
    "type": "user",
    "tags": ["preferences"],
    "created_at": "2026-01-10T09:00:00Z"
  }
]
POST /api/v1/memory

Create a memory entry. Memories are injected into AI prompts for context-aware responses.

Request body
{
  "content": "Customer Acme Corp renewed their enterprise contract for 3 years",
  "type": "business",
  "tags": ["customers", "contracts", "acme"]
}
Response
{
  "id": "mem_def456",
  "content": "Customer Acme Corp renewed their enterprise contract for 3 years",
  "type": "business",
  "tags": ["customers", "contracts", "acme"],
  "created_at": "2026-01-15T12:00:00Z"
}
POST /api/v1/memory/search

Semantic search across memories using Pinecone.

Request body
{
  "query": "What is Acme Corp's contract status?",
  "top_k": 5
}
Response
{
  "results": [
    {
      "id": "mem_def456",
      "content": "Customer Acme Corp renewed their enterprise contract for 3 years",
      "score": 0.94,
      "tags": ["customers", "contracts", "acme"]
    }
  ]
}
DELETE /api/v1/memory/{id}

Delete a memory entry.

Response
{ "deleted": true }

Integrations

GET /api/v1/integrations

List all available integrations and their connection status.

Example request
curl https://stryda.ai/api/v1/integrations \
  -H "x-api-key: YOUR_KEY"
Response
{
  "total": 228,
  "available": 5,
  "coming_soon": 223,
  "connected": 3,
  "integrations": [
    {
      "id": "int_slack",
      "name": "Slack",
      "category": "Communication",
      "connected": true,
      "connected_at": "2026-01-05T10:00:00Z"
    },
    {
      "id": "int_hubspot",
      "name": "HubSpot",
      "category": "CRM & Sales",
      "connected": false
    }
  ]
}

Templates

GET /api/v1/templates

List all available workflow templates.

Response
[
  {
    "id": "tpl_support_triage",
    "name": "Customer Support Triage",
    "category": "support",
    "node_count": 5,
    "estimated_cost_per_run": 0.003,
    "installs": 1247
  }
]
POST /api/v1/templates/{id}/instantiate

Create a new workflow from a template. Override any node configuration during instantiation.

Example request
curl -X POST https://stryda.ai/api/v1/templates/tpl_support_triage/instantiate \
  -H "x-api-key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "My Custom Triage"}'
Request body
{
  "name": "My Custom Triage",
  "overrides": {
    "nodes": {
      "draft_reply": {
        "config": { "model": "claude-sonnet-4-6", "temperature": 0.7 }
      }
    },
    "budget": { "per_run_cap": 0.25, "daily_cap": 15.00 }
  }
}
Response
{
  "workflow_id": "wf_new789",
  "name": "My Custom Triage",
  "template_id": "tpl_support_triage",
  "status": "draft",
  "created_at": "2026-01-15T12:00:00Z"
}

Webhooks

POST /hooks/{slug} Public

Trigger a workflow via webhook. The JSON body is passed as input_data. Supports HMAC-SHA256 signature verification.

Example request
curl -X POST https://stryda.ai/hooks/my-webhook-slug \
  -H "Content-Type: application/json" \
  -H "X-Stryda-Signature: sha256=abc123..." \
  -d '{"event": "new_order", "customer": "[email protected]"}'
Request body
{
  "event": "new_order",
  "customer": "[email protected]",
  "total": 99.99
}
Response
{ "status": "accepted", "run_id": "run_789" }

Error responses

All errors return a consistent JSON shape with a detail field (or an error object with code, message, and details). Use the HTTP status code to determine the category.

Example error (422)
{
  "detail": "Validation error: 'name' is required and must be a non-empty string."
}
Example error (503 budget exceeded)
{
  "error": {
    "code": "budget_exceeded",
    "message": "Monthly budget cap of $200.00 has been reached",
    "details": {
      "monthly_spent": 200.12,
      "monthly_cap": 200.00
    }
  }
}
StatusCodeDescription
400invalid_requestMissing or malformed request body
401unauthorizedMissing or invalid API key or bearer token
403forbiddenKey does not have access to this resource, or free plan without AI access
404not_foundResource does not exist or belongs to another user
409conflictResource already exists or state conflict
422validation_errorRequest body failed schema validation
429rate_limitedToo many requests — 60/min (API) or 30/min (embed)
500internal_errorUnexpected server error — retry with exponential backoff
503budget_exceededBudget cap reached, execution halted

Common mistakes

SDK examples

No official SDK yet — here are idiomatic patterns for common languages.

Python
import os, requests

API_KEY = os.environ["STRYDA_API_KEY"]
BASE = "https://stryda.ai/api/v1"

res = requests.post(
    f"{BASE}/workflows/wf_abc123/run",
    headers={"x-api-key": API_KEY, "Content-Type": "application/json"},
    json={"input_data": {"prompt": "Analyze this email"}},
)
run = res.json()
print(f"Run {run['run_id']} is {run['status']}")
TypeScript
const API_KEY = process.env.STRYDA_API_KEY!;
const BASE = 'https://stryda.ai/api/v1';

const res = await fetch(`${BASE}/workflows/wf_abc123/run`, {
  method: 'POST',
  headers: {
    'x-api-key': API_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    input_data: { prompt: 'Analyze this email' },
  }),
});
const run = await res.json();

console.log(`Run ${run.run_id} is ${run.status}`);
Calling MCP directly from a script
curl -sS -X POST https://mcp.stryda.ai/api/mcp \
  -H "Authorization: Bearer ${MCP_BEARER}" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'