Documentation Browse guides

Workflows & triggers

Save a sequence of tool calls, and act on events from connected apps and public data.

Define a workflow

A workflow has a slug (2–40 lowercase letters, digits or underscores, starting with a letter; required when you create it), a title, a description, an inputSchema and 1–20 steps. Each step is { id, tool, arguments, provider? }. In arguments, {"$input": "topic"} takes a run input and {"$step": "search", "path": "/results/0/url"} takes a value from an earlier step’s output.

workflow.json
{
  "slug": "research_brief",
  "title": "Research brief",
  "description": "Search the web for a topic and read the top result.",
  "inputSchema": {
    "type": "object",
    "properties": { "topic": { "type": "string" } },
    "required": ["topic"]
  },
  "steps": [
    { "id": "search", "tool": "web_search",
      "arguments": { "query": { "$input": "topic" } } },
    { "id": "read", "tool": "scrape_page",
      "arguments": { "url": { "$step": "search", "path": "/results/0/url" } } }
  ]
}

Save it with POST /v1/workflows. Every save is a new version and the newest 50 are kept.

Run a workflow

Start a run with inputs, a maximumChargeMicros budget and an Idempotency-Key. Each step is quoted and bought in order within what is left of the budget.

Shell
curl "$RELVO_API_URL/v1/workflows" \
  -H "Authorization: Bearer $RELVO_API_KEY" \
  -H "Content-Type: application/json" \
  --data @workflow.json

curl "$RELVO_API_URL/v1/workflows/research_brief/runs" \
  -H "Authorization: Bearer $RELVO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: research-brief-001" \
  --data '{"inputs":{"topic":"content marketing"},"maximumChargeMicros":200000}'

A step that no longer fits the budget stops the run with platform_workflow_budget_exceeded; earlier steps stay charged. Read the run with GET /v1/workflow-runs/{id} and cancel it with POST /v1/workflow-runs/{id}/cancel. A project holds up to 50 workflows and runs up to 10 at once.

Triggers

A poll trigger runs a read operation of a connected app every 1m, 5m (default), 15m or 1h and emits added for each new item, or changed when a single result changes. A webhook trigger uses a type such as github.push: Relvo registers the webhook through your connection. The first poll sets the baseline and emits nothing.

trigger.json
{
  "kind": "poll",
  "tool": "github__issues_list_for_repo",
  "arguments": { "owner": "octo", "repo": "app" },
  "connectionId": "CONNECTION_ID",
  "interval": "5m",
  "destinations": [{ "kind": "webhook", "url": "https://YOUR_SERVER/relvo/events" }]
}
Shell
curl "$RELVO_API_URL/v1/triggers" \
  -H "Authorization: Bearer $RELVO_API_KEY" \
  -H "Content-Type: application/json" \
  --data @trigger.json

GET /v1/trigger-types?app=github lists an app’s webhook types and the operations you can poll. A trigger runs as the API key that created it, and revoking that key stops the trigger.

Watch public data

A monitor re-runs a single-step tool that reads shared public data on a schedule (15m, 1h, 1d or a 5-field cron, at least 15 minutes apart) and emits only what changed. In the app it appears under Triggers.

Price it with POST /v1/monitors/quote, then create it with POST /v1/monitors and a maximumChargeMicrosPerRun of at least the quoted chargeCeilingMicros, plus 300 micros if it should keep running after your included calls are used. A monitor runs and charges until you delete it. It pauses while the balance, a spending limit or that maximum cannot cover a run, and resumes by itself once a run fits again. It also pauses when the price rises above your maximum, the project’s tool execution no longer allows the tool, or its creator loses access to the project; those pauses need POST /v1/monitors/{id}/resume. Revoking the key that created it does not stop it.

Destinations

Add up to 5 destinations when you create a trigger, or later with POST /v1/triggers/{id}/destinations:

Destinations
webhook
  Each event as a signed JSON POST to your HTTPS URL.

workflow
  Starts one run of a workflow per event, with the event as its event input and maximumChargeMicros as each run's budget.

slack / discord
  A summary of up to 10 new events with a link to the app, sent to an incoming webhook URL.

email
  A summary to team members. Added only in the app.

Failed deliveries (408, 429, 5xx and network errors) are tried up to 6 times in all. Retries wait 30 seconds and double each time, up to 8 minutes; a 429’s Retry-After is honored up to an hour. Other 4xx responses fail at once. A delivery can arrive twice, so deduplicate on Relvo-Delivery.

Verify webhook signatures

Each webhook delivery has a Relvo-Signature: t=<unix seconds>,v1=<hex> header and a Relvo-Delivery id. v1 is the HMAC-SHA256 of <t>.<raw body> with the destination’s signing secret, which is returned only once, when you add the destination. Check the timestamp and compare signatures in constant time.

Node.js
import { createHmac, timingSafeEqual } from "node:crypto";

// rawBody is the request body exactly as received, before JSON parsing.
export function verifyRelvoSignature(header, rawBody, secret, toleranceSeconds = 300) {
  const parts = Object.fromEntries(header.split(",").map(part => part.split("=", 2)));
  const timestamp = Number(parts.t);
  const age = Math.abs(Date.now() / 1000 - timestamp);
  if (!Number.isInteger(timestamp) || age > toleranceSeconds) return false;
  const expected = createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest();
  const received = Buffer.from(parts.v1 ?? "", "hex");
  return received.length === expected.length && timingSafeEqual(received, expected);
}

Events, pricing and limits

GET /v1/triggers/{id}/events and GET /v1/trigger-events return events oldest first after after; pass nextAfter as the next after. Without after you get the newest events; pass after=0 to start from the oldest kept event. Events are kept for 30 days, up to 10,000 per trigger.

Each poll is one tool call: it counts toward your plan’s included calls and then costs 300 micros. Receiving webhooks is free, email costs 900 micros per recipient, and a workflow destination is charged per step. Up to 50 triggers per project and 100 per team, and 25 monitors per project.

Endpoints

Endpoints
GET  /v1/workflows · tools:read
  List workflows

POST /v1/workflows · tools:execute
  Save a new workflow

GET  /v1/workflows/{slug} · tools:read
  Read one workflow

POST /v1/workflows/{slug}/update · tools:execute
  Replace it as a new version (send expectedVersion)

POST /v1/workflows/{slug}/delete · tools:execute
  Delete it

POST /v1/workflows/{slug}/runs · tools:execute
  Start a run

GET  /v1/workflow-runs/{id} · executions:read
  Read a run

POST /v1/workflow-runs/{id}/cancel · tools:execute
  Cancel a run

GET  /v1/trigger-types?app={app} · tools:read
  Webhook types and pollable operations of an app

GET  /v1/triggers · executions:read
  List triggers

POST /v1/triggers · tools:execute
  Create a trigger

POST /v1/triggers/{id}/update · tools:execute
  Enable, disable or rename it

POST /v1/triggers/{id}/delete · tools:execute
  Delete it

POST /v1/triggers/{id}/destinations · tools:execute
  Add a destination

GET  /v1/triggers/{id}/events · executions:read
  Events of one trigger

GET  /v1/trigger-events · executions:read
  Events of every trigger

POST /v1/monitors/quote · tools:read
  Price a monitor without creating it

GET  /v1/monitors · executions:read
  List monitors

POST /v1/monitors · tools:execute
  Create a monitor

POST /v1/monitors/{id}/resume · tools:execute
  Resume a paused monitor

POST /v1/monitors/{id}/delete · tools:execute
  Delete it

GET  /v1/monitor-events · executions:read
  Events of every monitor

These endpoints are under /v1 on the HTTP API and use the same keys and scopes.

Looking for a specific tool’s inputs and price? Search the catalog