Connect it to your app
Two directions — your app calling a workflow over HTTP, and a worker of yours providing functions the graph can call.
A workflow is only useful when something calls it, or when it can call something of yours. Both directions are worth knowing; most people need the first.
Direction 1: your app calls the workflow
The workflow has an HTTP endpoint and an API key. Your app POSTs to it like any other service:
// Always bound the call: Node's default timeout is five minutes,
// which makes a hung workflow look like a hung app.
const ac = new AbortController();
const timer = setTimeout(() => ac.abort(), 60_000);
const res = await fetch(
`${process.env.WORKFLOW_URL}`,
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.WORKFLOW_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ question: userInput }),
signal: ac.signal,
},
).finally(() => clearTimeout(timer)); Put the URL and key in secrets, not in the image — see Rotate secrets and env vars.
An agent workflow can take tens of seconds. Rather than holding a request
open, start it with --async (or the equivalent HTTP call), store the run
id, and fetch the output when it finishes. dibbla wf runs output <runId>
is the CLI form of that fetch.
Direction 2: the workflow calls your code
Functions in the registry come from execution servers — long-lived worker processes that connect over gRPC, announce what they provide, and stay connected. You write one with the Go SDK:
package main
import (
"log"
"os"
sdk "github.com/dibbla-agents/sdk-go"
)
func main() {
server, err := sdk.New(
sdk.WithServerName("my-worker"),
sdk.WithServerApiToken(os.Getenv("SERVER_API_TOKEN")),
)
if err != nil {
log.Fatal(err)
}
// Register before Start(): registration is what advertises them.
server.RegisterFunction(myFunction)
// Start() blocks forever, serving dispatches.
if err := server.Start(); err != nil {
log.Fatal(err)
}
} Once it connects, your functions appear in dibbla functions list and become
selectable in the editor. They are addressed by their (server, function)
pair, which is why the server name matters.
The SDK defaults to grpc.dibbla.com:443 — production. A worker you meant to
point at a development environment will connect to production instead, register
there, and quietly start serving real traffic. Always set the address
explicitly, in every environment, including the one you think is obvious.
# the gRPC listener is cluster-internal; forward it first # on a Helm self-host the service is <release>-workflow-server $kubectl -n dibbla-agents port-forward svc/workflow-server-service 50051:50051 & $SERVER_NAME=my-worker \ $GRPC_SERVER_ADDRESS=127.0.0.1:50051 \ $GRPC_USE_TLS=false \ $SERVER_API_TOKEN="$TOKEN" go run . Registered 1 functions
A connected server shows up on the dashboard with its function count. If your node says the function is unknown, the worker is not connected — that is the first thing to check, before the YAML.
See Custom functions for the handler signatures themselves.