Sevro

API Reference

Sevro's Connect-RPC API for managing modules, records, connections, webhooks, and workflows.

Sevro exposes a Connect-RPC API. Connect-RPC is gRPC-compatible but works over HTTP/1.1 + JSON, so you can call it with curl or any HTTP client — no special tooling required.

Authentication

All API calls require a Bearer token in the Authorization header:

Authorization: Bearer ply_<your-api-key>

API keys are created with sevro bootstrap (first key) or the AuthService.CreateAPIKey RPC (subsequent keys).

Base URL

The API is served at the root of the Sevro server. RPC endpoints follow the Connect-RPC convention:

POST /<package>.<Service>/<Method>

For example:

POST /platform.v1.PlatformService/CreateModule
POST /platform.v1.PlatformService/ListRecords
POST /auth.v1.AuthService/CreateAPIKey

Content types

Connect-RPC supports two wire formats:

  • JSON (Content-Type: application/json) — human-readable, use for curl
  • Protobuf (Content-Type: application/proto) — binary, smaller, faster

All examples below use JSON.

PlatformService

Design

CreateModule

curl -X POST http://localhost:8080/platform.v1.PlatformService/CreateModule \
  -H "Authorization: Bearer ply_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "todo",
    "entities": [{
      "ref": "todo",
      "label": "Todo",
      "attributes": [
        {"ref": "description", "label": "Description", "type": "string", "isRequired": true}
      ],
      "methods": [{
        "ref": "new",
        "label": "New",
        "isConstructor": true,
        "parameters": [
          {"ref": "description", "label": "Description", "type": "string", "isRequired": true}
        ],
        "actions": [
          {"target": "description", "expression": "params.description"}
        ]
      }]
    }]
  }'

Response: {"id": "module-uuid"}

UpdateModule

curl -X POST http://localhost:8080/platform.v1.PlatformService/UpdateModule \
  -H "Authorization: Bearer ply_..." \
  -H "Content-Type: application/json" \
  -d '{"id": "module-uuid", "name": "todo", "entities": [...], "workflows": [...]}'

GetModule

curl -X POST http://localhost:8080/platform.v1.PlatformService/GetModule \
  -H "Authorization: Bearer ply_..." \
  -H "Content-Type: application/json" \
  -d '{"id": "module-uuid"}'

ListModules

curl -X POST http://localhost:8080/platform.v1.PlatformService/ListModules \
  -H "Authorization: Bearer ply_..." \
  -H "Content-Type: application/json" \
  -d '{"pageSize": 100}'

DeleteModule

curl -X POST http://localhost:8080/platform.v1.PlatformService/DeleteModule \
  -H "Authorization: Bearer ply_..." \
  -H "Content-Type: application/json" \
  -d '{"id": "module-uuid"}'

Runtime

ExecuteMethod

Execute a method on a record (or create via constructor).

# Create a todo (constructor)
curl -X POST http://localhost:8080/platform.v1.PlatformService/ExecuteMethod \
  -H "Authorization: Bearer ply_..." \
  -H "Content-Type: application/json" \
  -d '{
    "moduleId": "module-uuid",
    "entityRef": "todo",
    "methodRef": "new",
    "arguments": {
      "description": "Buy groceries"
    }
  }'

# Complete a todo (non-constructor — requires recordId)
curl -X POST http://localhost:8080/platform.v1.PlatformService/ExecuteMethod \
  -H "Authorization: Bearer ply_..." \
  -H "Content-Type: application/json" \
  -d '{
    "moduleId": "module-uuid",
    "entityRef": "todo",
    "methodRef": "complete",
    "recordId": "record-uuid"
  }'

Response: {"recordId": "record-uuid"}

GetRecord

curl -X POST http://localhost:8080/platform.v1.PlatformService/GetRecord \
  -H "Authorization: Bearer ply_..." \
  -H "Content-Type: application/json" \
  -d '{"id": "record-uuid", "moduleId": "module-uuid", "entityRef": "todo"}'

Use expand to resolve reference fields:

{
  "id": "record-uuid",
  "moduleId": "...",
  "entityRef": "project",
  "expand": "owner_id,tasks"
}

ListRecords

curl -X POST http://localhost:8080/platform.v1.PlatformService/ListRecords \
  -H "Authorization: Bearer ply_..." \
  -H "Content-Type: application/json" \
  -d '{
    "moduleId": "module-uuid",
    "entityRef": "todo",
    "filter": "record.completed == false",
    "pageSize": 50
  }'

The filter field accepts a CEL expression evaluated against each record. The expand field resolves references (same as GetRecord).

DeleteRecord

curl -X POST http://localhost:8080/platform.v1.PlatformService/DeleteRecord \
  -H "Authorization: Bearer ply_..." \
  -H "Content-Type: application/json" \
  -d '{"id": "record-uuid"}'

GetRecordHistory

Retrieve the audit trail for a record.

curl -X POST http://localhost:8080/platform.v1.PlatformService/GetRecordHistory \
  -H "Authorization: Bearer ply_..." \
  -H "Content-Type: application/json" \
  -d '{"recordId": "record-uuid", "pageSize": 50}'

Workflows

ExecuteWorkflow

curl -X POST http://localhost:8080/platform.v1.PlatformService/ExecuteWorkflow \
  -H "Authorization: Bearer ply_..." \
  -H "Content-Type: application/json" \
  -d '{
    "moduleId": "module-uuid",
    "workflowRef": "notify_created",
    "input": {
      "todo_id": "record-uuid"
    }
  }'

Response: {"executionId": "...", "status": "completed", "output": {...}}

GetWorkflowStatus

curl -X POST http://localhost:8080/platform.v1.PlatformService/GetWorkflowStatus \
  -H "Authorization: Bearer ply_..." \
  -H "Content-Type: application/json" \
  -d '{"executionId": "execution-uuid"}'

Connections

RPCDescription
CreateConnectionCreate a named external service connection with credentials
UpdateConnectionUpdate connection config and credentials
DeleteConnectionDelete a connection
GetConnectionGet connection metadata (credentials are never returned)
ListConnectionsList all connections
TestConnectionTest that a connection's credentials are valid

Webhooks (outbound)

RPCDescription
CreateWebhookRegister an outbound webhook for entity events
UpdateWebhookUpdate webhook URL, events, or active status
DeleteWebhookDelete a webhook
GetWebhookGet webhook details
ListWebhooksList all webhooks
ListWebhookDeliveriesList delivery attempts for a webhook

Inbound webhooks

RPCDescription
CreateInboundWebhookRegister an inbound webhook that maps external POST → method execution
UpdateInboundWebhookUpdate transform expression or active status
DeleteInboundWebhookDelete an inbound webhook
GetInboundWebhookGet inbound webhook details (secret is not returned)
ListInboundWebhooksList all inbound webhooks

Inbound webhooks also have a dedicated HTTP endpoint:

POST /hooks/{webhookID}

This endpoint receives raw POST requests from external services. It verifies the HMAC-SHA256 signature (via X-Sevro-Signature, X-Hub-Signature-256, or X-Webhook-Signature header), evaluates the CEL transform expression against the parsed JSON body, and executes the configured method. No Bearer token required — auth is via the webhook secret.

Example transform:

transform: "{'description': body.event_text, 'source': 'webhook'}"

The body variable is the parsed JSON payload from the incoming request. The transform must return a map[string]any of method arguments.

AuthService

RPCDescription
CreateAPIKeyCreate a new API key (returns raw token once)
RevokeAPIKeyRevoke an API key by ID
ListAPIKeysList all API keys (tokens are never returned)
CreatePolicyCreate an authorization policy (CEL expression)
UpdatePolicyUpdate a policy's expression
DeletePolicyDelete a policy
AssignPolicyAssign a policy to a role, method, or entity
UnassignPolicyRemove a policy assignment
ListPoliciesList all policies
ListPolicyAssignmentsList policy assignments

Error handling

Errors follow the Connect-RPC error format with structured details:

{
  "code": "invalid_argument",
  "message": "validation failed",
  "details": [
    {
      "@type": "type.googleapis.com/google.rpc.BadRequest",
      "fieldViolations": [
        { "field": "name", "description": "must not be empty" },
        {
          "field": "entities[0].attributes[1].type",
          "description": "must be one of [string, number, boolean, date, datetime, enum, reference]"
        }
      ]
    }
  ]
}

Error codes

CodeHTTP StatusMeaning
invalid_argument400Input validation failed
not_found404Resource not found
already_exists409Duplicate resource
unauthenticated401Missing or invalid API key
permission_denied403Authenticated but not allowed
internal500Server error (message is generic)

Calling from TypeScript

See the TypeScript integration guide for using @connectrpc/connect-web to call the API with full type safety.

On this page