Ava Protocol Workflows API
Workflow CRUD and lifecycle actions
Workflow CRUD and lifecycle actions
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/ava-protocol-workflows-api"
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
openapi: 3.0.3
info:
title: Ava Protocol AVS Auth Workflows API
version: 1.0.0
description: 'Public REST API for the Ava Protocol AVS aggregator. Exposes workflow
creation, execution monitoring, smart-wallet management, and related
operations.
Authentication is a single credential type — a JWT bearer token — obtained
either via the wallet-signing flow (`POST /auth:exchange`) or out-of-band
via the operator-run `create-api-key` CLI. Every request must include
`Authorization: Bearer <jwt>`.
'
servers:
- url: https://gateway.avaprotocol.org/api/v1
description: Production gateway
- url: https://gateway-staging.avaprotocol.org/api/v1
description: Staging gateway
- url: http://localhost:8080/api/v1
description: Local dev
security:
- bearerAuth: []
tags:
- name: Workflows
description: Workflow CRUD and lifecycle actions
paths:
/workflows:
post:
tags:
- Workflows
summary: Create a workflow
description: 'Persist a new workflow definition. Each chain-aware trigger and node
carries its own required `chainId` (there is no workflow-level chain);
the server validates those chains and ensures the smart wallet belongs
to the authenticated user. Returns the persisted Workflow with its
server-assigned `id` and `createdAt`.
'
operationId: createWorkflow
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateWorkflowRequest'
responses:
'201':
description: Workflow created.
content:
application/json:
schema:
$ref: '#/components/schemas/Workflow'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'429':
$ref: '#/components/responses/RateLimited'
get:
tags:
- Workflows
summary: List workflows
operationId: listWorkflows
parameters:
- name: smartWalletAddress
in: query
description: Filter by smart wallet address. Repeat to OR multiple addresses.
schema:
type: array
items:
$ref: '#/components/schemas/EthereumAddress'
- name: status
in: query
description: Filter by status. Repeat to OR multiple statuses.
schema:
type: array
items:
$ref: '#/components/schemas/WorkflowStatus'
- $ref: '#/components/parameters/PageBefore'
- $ref: '#/components/parameters/PageAfter'
- $ref: '#/components/parameters/PageLimit'
responses:
'200':
description: Page of workflows.
content:
application/json:
schema:
$ref: '#/components/schemas/WorkflowList'
'401':
$ref: '#/components/responses/Unauthorized'
'429':
$ref: '#/components/responses/RateLimited'
/workflows/{id}:
parameters:
- name: id
in: path
required: true
schema:
$ref: '#/components/schemas/Ulid'
get:
tags:
- Workflows
summary: Retrieve a workflow
operationId: getWorkflow
responses:
'200':
description: The workflow.
content:
application/json:
schema:
$ref: '#/components/schemas/Workflow'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
delete:
tags:
- Workflows
summary: Cancel a workflow
description: 'Permanently cancels and removes the workflow. SDK method is
`cancel(id)` to align with Stripe-style vocabulary.
'
operationId: cancelWorkflow
responses:
'204':
description: Workflow cancelled.
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
/workflows/{id}:pause:
parameters:
- name: id
in: path
required: true
schema:
$ref: '#/components/schemas/Ulid'
post:
tags:
- Workflows
summary: Pause a workflow
description: Transition from `enabled` to `disabled`. Idempotent.
operationId: pauseWorkflow
responses:
'200':
description: Workflow paused.
content:
application/json:
schema:
$ref: '#/components/schemas/Workflow'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'409':
description: Workflow is in a terminal state and cannot be paused.
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
/workflows/{id}:resume:
parameters:
- name: id
in: path
required: true
schema:
$ref: '#/components/schemas/Ulid'
post:
tags:
- Workflows
summary: Resume a workflow
description: Transition from `disabled` to `enabled`. Idempotent.
operationId: resumeWorkflow
responses:
'200':
description: Workflow resumed.
content:
application/json:
schema:
$ref: '#/components/schemas/Workflow'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'409':
description: Workflow is in a terminal state and cannot be resumed.
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
/workflows/{id}:trigger:
parameters:
- name: id
in: path
required: true
schema:
$ref: '#/components/schemas/Ulid'
post:
tags:
- Workflows
summary: Manually trigger a workflow
description: 'Simulate an operator-detected trigger fire. The server enqueues an
execution; when `isBlocking=true`, the response waits for the
execution to complete.
'
operationId: triggerWorkflow
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TriggerWorkflowRequest'
responses:
'202':
description: Execution enqueued (async). Returned when isBlocking=false.
content:
application/json:
schema:
$ref: '#/components/schemas/TriggerWorkflowResponse'
'200':
description: Execution completed (blocking). Returned when isBlocking=true.
content:
application/json:
schema:
$ref: '#/components/schemas/TriggerWorkflowResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'412':
description: Workflow is disabled or has reached its max execution count.
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
/workflows:simulate:
post:
tags:
- Workflows
summary: Simulate a workflow without persisting it
description: 'Run a workflow definition end-to-end against the engine (Tenderly
simulation for chain-writing nodes) and return the full Execution.
Nothing is persisted.
'
operationId: simulateWorkflow
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SimulateWorkflowRequest'
responses:
'200':
description: Simulated execution.
content:
application/json:
schema:
type: object
additionalProperties: true
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
/workflows:estimateFees:
post:
tags:
- Workflows
summary: Estimate per-execution fees for a workflow definition
description: 'Returns the platform execution fee, per-node COGS (gas, external
API costs), and the workflow-level value-capture fee tier.
'
operationId: estimateWorkflowFees
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/EstimateFeesRequest'
responses:
'200':
description: Fee estimate.
content:
application/json:
schema:
$ref: '#/components/schemas/EstimateFeesResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
/workflows:count:
get:
tags:
- Workflows
summary: Count workflows
description: Cheap aggregation over the same filter set as `GET /workflows`.
operationId: countWorkflows
parameters:
- name: smartWalletAddress
in: query
schema:
type: array
items:
$ref: '#/components/schemas/EthereumAddress'
- name: status
in: query
schema:
type: array
items:
$ref: '#/components/schemas/WorkflowStatus'
responses:
'200':
description: Workflow count for the filter set.
content:
application/json:
schema:
$ref: '#/components/schemas/WorkflowCount'
'401':
$ref: '#/components/responses/Unauthorized'
components:
schemas:
SimulateWorkflowRequest:
type: object
required:
- trigger
- nodes
- inputVariables
properties:
chainId:
$ref: '#/components/schemas/ChainId'
trigger:
$ref: '#/components/schemas/Trigger'
nodes:
type: array
items:
$ref: '#/components/schemas/Node'
edges:
type: array
items:
$ref: '#/components/schemas/Edge'
inputVariables:
$ref: '#/components/schemas/InputVariables'
PageInfo:
type: object
required:
- hasNextPage
- hasPreviousPage
properties:
hasNextPage:
type: boolean
hasPreviousPage:
type: boolean
startCursor:
type: string
description: Cursor for the first item in the current page; pass to `before` for the previous page.
endCursor:
type: string
description: Cursor for the last item in the current page; pass to `after` for the next page.
EventTriggerQuery:
type: object
description: Single ethereum.FilterQuery — one subscription per query.
properties:
addresses:
type: array
items:
$ref: '#/components/schemas/EthereumAddress'
description: Contract addresses to filter events from. Empty matches any contract.
topics:
type: array
items:
type: string
nullable: true
description: 'Topic filters (`topics[0]` is the event signature, `topics[1..]`
are indexed parameter values). `null` means wildcard at that
position.
'
maxEventsPerBlock:
type: integer
format: int32
description: Safety ceiling per query per block. Exceeded → task cancelled.
contractAbi:
type: array
items:
additionalProperties: true
description: Contract ABI entries (JSON form) for event decoding.
conditions:
type: array
items:
$ref: '#/components/schemas/EventCondition'
description: Filters applied to decoded event data.
methodCalls:
type: array
items:
$ref: '#/components/schemas/EventMethodCall'
description: Method calls used to enrich decoded event data (e.g., `decimals`).
EventCondition:
type: object
description: Predicate evaluated against decoded event data.
required:
- fieldName
- operator
- value
properties:
fieldName:
type: string
operator:
type: string
enum:
- eq
- ne
- gt
- gte
- lt
- lte
- contains
value:
type: string
description: 'Value to compare against, encoded as a string. The operator
parses it according to `fieldType` (e.g. `int256` / `uint256`
→ big.Int, `address` → checksummed hex, `bool` →
"true"/"false"). Matches the proto `EventCondition.value`,
which is also a string.
'
fieldType:
type: string
WorkflowStatus:
type: string
enum:
- enabled
- disabled
- running
- completed
- failed
description: 'Lifecycle status. `enabled` means actively monitored; `disabled` is
paused. `running`, `completed`, `failed` are terminal-ish states
emitted during/after execution.
'
FilterNodeConfig:
type: object
required:
- inputVariable
- expression
properties:
inputVariable:
type: string
description: Template path for the source array (e.g., `{{custom_code1.data}}`).
expression:
type: string
description: JavaScript predicate evaluated per item.
ContractReadNode:
allOf:
- type: object
properties:
type:
type: string
enum:
- contractRead
config:
$ref: '#/components/schemas/ContractReadNodeConfig'
EthereumAddress:
type: string
pattern: ^0x[a-fA-F0-9]{40}$
description: Lowercase or checksummed hex EOA / contract address.
example: '0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB'
InputVariables:
type: object
additionalProperties: true
description: 'Free-form key-value bag of values used to resolve `{{variable.path}}`
template references inside trigger and node configs. Conventional
well-known keys: `settings.runner` (smart wallet address),
`settings.chainId` (chain id). camelCase keys; back-compat support
for snake_case keys exists during the migration window.
'
RestAPINodeConfig:
type: object
required:
- url
- method
properties:
url:
type: string
format: uri
method:
type: string
enum:
- GET
- POST
- PUT
- PATCH
- DELETE
- HEAD
- OPTIONS
headers:
type: object
additionalProperties:
type: string
body:
type: string
options:
type: object
description: 'Generic options bag for backend features on terminal
RestAPI nodes. `summarize: true` opts a SendGrid /v3/mail/send
or Telegram /sendMessage node into the aggregator''s
context-memory summarizer, which composes a subject + HTML
body from the workflow''s execution context and injects them
into the outgoing request. Without this field set, the
aggregator falls back to the deterministic summarizer (no
LLM polish).
'
properties:
summarize:
type: boolean
description: 'When true on a terminal SendGrid or Telegram node,
ComposeSummarySmart runs at execution time and fills
in the empty content.value / text slot with an
AI-generated body. No-op on non-notification URLs.
'
additionalProperties: true
TriggerWorkflowRequest:
type: object
required:
- triggerType
properties:
triggerType:
$ref: '#/components/schemas/TriggerType'
triggerOutput:
description: 'Type-specific output payload (BlockTrigger.Output,
EventTrigger.Output, etc.) that simulates what the operator
would have observed. Defined alongside execution schemas.
'
additionalProperties: true
triggerInput:
$ref: '#/components/schemas/InputVariables'
isBlocking:
type: boolean
description: When true, wait for the execution to complete and return its result.
CustomCodeNodeConfig:
type: object
required:
- lang
- source
properties:
lang:
$ref: '#/components/schemas/Lang'
source:
type: string
MethodCall:
type: object
description: One call to a contract method (used by ContractWrite + ContractRead).
required:
- methodName
properties:
methodName:
type: string
callData:
$ref: '#/components/schemas/Hex'
applyToFields:
type: array
items:
type: string
methodParams:
type: array
items:
type: string
description: Handlebars template for method args.
Hex:
type: string
pattern: ^0x[a-fA-F0-9]*$
description: Arbitrary-length hex-encoded byte string.
ETHTransferNodeConfig:
type: object
required:
- destination
- amount
- chainId
properties:
destination:
$ref: '#/components/schemas/EthereumAddress'
amount:
type: string
description: Amount in wei (decimal string for big-int safety). Special value `max` withdraws the entire balance.
chainId:
$ref: '#/components/schemas/ChainId'
description: Chain to execute on. Required — a workflow carries no chain to inherit.
EventTrigger:
allOf:
- type: object
properties:
type:
type: string
enum:
- event
config:
$ref: '#/components/schemas/EventTriggerConfig'
TriggerType:
type: string
enum:
- manual
- fixedTime
- cron
- block
- event
description: 'Discriminator field for the Trigger union. Mirrors the proto
`TriggerType` enum but without the `TRIGGER_TYPE_` prefix.
'
EstimateFeesRequest:
type: object
required:
- trigger
- nodes
- createdAt
- expireAt
- maxExecution
properties:
chainId:
$ref: '#/components/schemas/ChainId'
trigger:
$ref: '#/components/schemas/Trigger'
nodes:
type: array
items:
$ref: '#/components/schemas/Node'
edges:
type: array
items:
$ref: '#/components/schemas/Edge'
runner:
$ref: '#/components/schemas/EthereumAddress'
description: Smart wallet address used for gas estimation (overrides settings.runner).
createdAt:
type: integer
format: int64
expireAt:
type: integer
format: int64
maxExecution:
type: integer
format: int64
inputVariables:
$ref: '#/components/schemas/InputVariables'
EventTriggerConfig:
type: object
description: Fires when matching on-chain events are observed.
required:
- queries
- chainId
properties:
queries:
type: array
items:
$ref: '#/components/schemas/EventTriggerQuery'
minItems: 1
cooldownSeconds:
type: integer
format: int32
minimum: 0
description: 'Seconds to wait after a fire before allowing the same task to
trigger again. Default 300. 0 disables cooldown.
'
chainId:
$ref: '#/components/schemas/ChainId'
description: Chain to watch events on. Required — a workflow carries no chain to inherit.
EstimateFeesResponse:
type: object
required:
- chainId
- executionFee
- cogs
- valueFee
properties:
chainId:
$ref: '#/components/schemas/ChainId'
nativeToken:
$ref: '#/components/schemas/NativeToken'
executionFee:
$ref: '#/components/schemas/Fee'
description: Flat per-execution platform fee (typically USD).
cogs:
type: array
items:
$ref: '#/components/schemas/NodeCOGS'
description: Per-node operational costs (gas, external API).
valueFee:
$ref: '#/components/schemas/ValueFee'
description: Workflow-level value-capture fee (PERCENTAGE).
discounts:
type: array
items:
$ref: '#/components/schemas/FeeDiscount'
pricingModel:
type: string
description: Pricing model label (e.g., `tiered_value_capture_v1`).
CronTriggerConfig:
type: object
description: Fires on one or more cron schedules.
required:
- schedules
properties:
schedules:
type: array
items:
type: string
description: Standard cron expression (e.g., `* * * * *`).
minItems: 1
timezone:
type: string
description: IANA timezone (e.g., `UTC`, `America/New_York`). Default UTC.
Fee:
type: object
required:
- amount
- unit
properties:
amount:
type: string
description: Decimal numeric value, encoded as a string for big-int safety.
unit:
type: string
enum:
- USD
- WEI
- PERCENTAGE
ETHTransferNode:
allOf:
- type: object
properties:
type:
type: string
enum:
- ethTransfer
config:
$ref: '#/components/schemas/ETHTransferNodeConfig'
FeeDiscount:
type: object
properties:
discountType:
type: string
enum:
- newUser
- volume
- promotional
- betaProgram
discountName:
type: string
discount:
$ref: '#/components/schemas/Fee'
expiryDate:
$ref: '#/components/schemas/Timestamp'
terms:
type: string
BalanceNodeConfig:
type: object
required:
- address
- chain
properties:
address:
$ref: '#/components/schemas/EthereumAddress'
chain:
type: string
description: Chain name or numeric ID (e.g., `ethereum`, `base`, `1`, `8453`).
includeSpam:
type: boolean
includeZeroBalances:
type: boolean
minUsdValueCents:
type: integer
format: int64
description: Filter out tokens with USD value below this many cents.
tokenAddresses:
type: array
items:
$ref: '#/components/schemas/EthereumAddress'
description: Restrict to these tokens. Empty = fetch all.
BlockTrigger:
allOf:
- type: object
properties:
type:
type: string
enum:
- block
config:
$ref: '#/components/schemas/BlockTriggerConfig'
GraphQLQueryNode:
allOf:
- type: object
properties:
type:
type: string
enum:
- graphqlQuery
config:
$ref: '#/components/schemas/GraphQLQueryNodeConfig'
Ulid:
type: string
pattern: ^[0-9A-HJKMNP-TV-Z]{26}$
description: ULID identifier (26-char Crockford base32).
example: 01JG2FE5MDVKBPHEG0PEYSDKAC
FixedTimeTrigger:
allOf:
- type: object
properties:
type:
type: string
enum:
- fixedTime
config:
$ref: '#/components/schemas/FixedTimeTriggerConfig'
TriggerWorkflowResponse:
type: object
required:
- executionId
- status
properties:
executionId:
$ref: '#/components/schemas/Ulid'
status:
$ref: '#/components/schemas/ExecutionStatus'
startAt:
type: integer
format: int64
endAt:
type: integer
format: int64
error:
type: string
BranchNode:
allOf:
- type: object
properties:
type:
type: string
enum:
- branch
config:
$ref: '#/components/schemas/BranchNodeConfig'
ExecutionStatus:
type: string
enum:
- pending
- waiting
- success
- failed
- error
description: 'Outcome of an execution. `pending` is in-flight; `waiting` is suspended
mid-workflow at an `await` node, durably parked until a signal arrives
(a human approve/reject or an operator-observed chain event) or the wait
times out — non-terminal, like `pending`, but distinguishable so a client
can show "awaiting approval"; `success` is full success; `failed` is
logical failure (e.g., a node returned an error, or a wait timed out);
`error` is a system / infrastructure failure (e.g., RPC unreachable).
'
Timestamp:
type: string
format: date-time
description: RFC 3339 timestamp.
LoopNode:
allOf:
- type: object
properties:
type:
type: string
enum:
- loop
config:
$ref: '#/components/schemas/LoopNodeConfig'
CronTrigger:
allOf:
- type: object
properties:
type:
type: string
enum:
- cron
config:
$ref: '#/components/schemas/CronTriggerConfig'
BranchNodeConfig:
type: object
required:
- conditions
properties:
conditions:
type: array
items:
$ref: '#/components/schemas/BranchCondition'
minItems: 1
NodeCOGS:
type: object
required:
- nodeId
- costType
- fee
properties:
nodeId:
type: string
costType:
type: string
enum:
- gas
- externalApi
- walletCreation
fee:
$ref: '#/components/schemas/Fee'
gasUnits:
type: string
description: Gas units (for `gas` cost type only).
NodeType:
type: string
enum:
- ethTransfer
- contractWrite
- contractRead
- graphqlQuery
- restApi
- customCode
- branch
- filter
- loop
- balance
- await
description: 'Discriminator field for the Node union. Mirrors the proto
`NodeType` enum but without the `NODE_TYPE_` prefix.
'
ContractWriteNodeConfig:
type: object
required:
- contractAddress
- chainId
properties:
contractAddress:
$ref: '#/components/schemas/EthereumAddress'
callData:
$ref: '#/components/schemas/Hex'
contractAbi:
type: array
items:
additionalProperties: true
methodCalls:
type: array
items:
$ref: '#/components/schemas/MethodCall'
isSimulated:
type: boolean
description: When true, use Tenderly simulation instead of sending a real UserOp.
value:
type: string
description: ETH value to send with the call (wei, decimal string).
gasLimit:
type: string
description: Custom gas limit (decimal string).
chainId:
$ref: '#/components/schemas/ChainId'
description: Chain to execute on. Required — a workflow carries no chain to inherit.
NativeToken:
type: object
required:
- symbol
- decimals
properties:
symbol:
type: string
example: ETH
decimals:
type: integer
format: int32
ManualTriggerConfig:
type: object
description: User-initiated trigger; no chain context.
required:
- lang
properties:
data:
description: Arbitrary structured payload returned in the trigger output.
additionalProperties: true
headers:
type: object
additionalProperties:
type: string
description: HTTP headers (for webhook testing).
pathParams:
type: object
additionalProperties:
type: string
description: Path parameters (for webhook testing).
lang:
$ref: '#/components/schemas/Lang'
Lang:
type: string
enum:
- javascript
- json
- graphql
- handlebars
description: 'Language/format of an inline payload (e.g., custom code source,
manual trigger data). Mirrors the proto `Lang` enum minus the
`LANG_` prefix. Wire values are lowercase.
'
FilterNode:
allOf:
- type: object
properties:
type:
type: string
enum:
- filter
config:
$ref: '#/components/schemas/FilterNodeConfig'
WorkflowList:
type: object
required:
- data
- pageInfo
properties:
data:
type: array
items:
$ref: '#/components/schemas/Workflow'
pageInfo:
$ref: '#/components/schemas/PageInfo'
ContractReadNodeConfig:
type: object
required:
- contractAddress
- chainId
properties:
contractAddress:
$ref: '#/components/schemas/EthereumAddress'
contractAbi:
type: array
items:
additionalProperties: true
methodCalls:
type: array
items:
$ref: '#/components/schemas/MethodCall'
chainId:
$ref: '#/components/schemas/ChainId'
description: Chain to read from. Required — a workflow carries no chain to inherit.
AwaitNodeConfig:
type: object
description: 'Pauses the workflow until a wake arrives (durable execution). Two mutually
exclusive flavors: the external-signal flavor (human approval — set `channel`,
e.g. a Telegram approve/reject), or the chain-event flavor (cross-chain — set
`chainEvent` to pause until an operator observes that on-chain event, e.g. a
bridge arrival on another chain). Exactly one flavor must be configured.
'
properties:
channel:
type: string
description: 'External-signal flavor — signal channel: `telegram` or `api`.'
approvers:
type: array
items:
type: string
description: External-signal flavor — authorized approver identities. Empty = the workflow owner.
prompt:
type: string
description: External-signal flavor — message shown to the approver.
chainEvent:
allOf:
- $ref: '#/components/schemas/EventTriggerConfig'
description: 'Chain-event flavor — the on-chain event to wait for (a mid-workflow
EventTrigger). An operator covering `chainEvent.chainId` watches it and
resumes the execution when it fires. Mutually exclusive with `channel`.
'
timeoutSeconds:
type: integer
format: int64
description: Safety bound; 0 = server default (the wait is never unbounded).
Edge:
type: object
required:
- id
- source
- target
properties:
id:
type: string
source:
type: string
description: Node or trigger ID where this edge starts.
target:
type: string
description: Node ID where this edge ends.
Workflow:
type: o
# --- truncated at 32 KB (45 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/ava-protocol/refs/heads/main/openapi/ava-protocol-workflows-api-openapi.yml