Quickstart
You start the server, run the hello example worker, invoke its Task, and read the Run.
You need Go 1.26, make, and Docker for PostgreSQL 18. Runcoil runs its tools through go run at pinned versions, so nothing installs globally.
Start the server
Clone the server repository, start PostgreSQL, create the tables, and start the server.
git clone https://github.com/runcoil/runcoil.git
cd runcoil
make dc-up # PostgreSQL 18 on port 5432
make run-migrate # create the tables; running it again changes nothing
make run-runcoil # the server on port 7070, with the token dev-tokenThe server now listens on port 7070. dev-token is for local work only. The server has no default token and refuses to start without one.
TIP
If the server stops at start with relation "schedules" does not exist, the tables are missing. Run make run-migrate.
Start a worker
Open a second terminal. Clone the Go SDK anywhere. The server and the SDK do not need sibling directories.
git clone https://github.com/runcoil/runcoil-go.git
cd runcoil-go
go run ./examples/hello # worker ID "hello", one Task "hello"The worker connects to http://localhost:7070 with the token dev-token. It reads RUNCOIL_ENDPOINT and RUNCOIL_TOKEN if you set them. When it registers, it logs a line like this:
level=INFO msg="worker started" worker_id=helloThe server logs that the worker registered. Leave both terminals running.
Invoke the Task
In a third terminal, invoke the Task. The input and the output are JSON, sent as base64.
curl -s -X POST http://localhost:7070/runcoil.v1.RunService/Invoke \
-H 'Authorization: Bearer dev-token' -H 'Content-Type: application/json' \
-d '{"workerId":"hello","taskId":"hello","input":"eyJuYW1lIjoiQWRhIn0=","idempotencyKey":"hello-1"}'
# {"runId":"..."}eyJuYW1lIjoiQWRhIn0= is {"name":"Ada"}. The idempotencyKey is required. If you send the same key and input again, the server returns the first Run instead of creating another. Use a new key for a new Run.
The answer holds the ID of the new Run. The worker logs that it handled the Run:
level=INFO msg=greeted run_id=<run id> attempt=1 name=AdaRead the Run
Pass the ID from the answer to WaitRun. The call returns when the Run finishes.
curl -s -X POST http://localhost:7070/runcoil.v1.RunService/WaitRun \
-H 'Authorization: Bearer dev-token' -H 'Content-Type: application/json' \
-d '{"runId":"<run id>","includeSteps":true}'With includeSteps set, the answer lists the Steps of the Run. The hello handler has one Step, greet.
The output of the Run is base64 JSON. Decode it to see the result:
echo 'eyJncmVldGluZyI6ImhlbGxvLCBBZGEifQ==' | base64 -d
# {"greeting":"hello, Ada"}If the answer has timedOut set to true, the Run was still going when the server stopped waiting. Send the same WaitRun call again.
Invoke from Go
The Go SDK has the same calls. Client.Invoke creates a Run, and Client.WaitRun waits for it. The invoke example uses them against the onboarding example, which is a worker with the worker ID accounts. Start that worker in another terminal:
go run ./examples/onboarding # worker ID "accounts", Task "create-account"Then run the invoke program from the SDK checkout in a third terminal:
go run ./examples/invokeOn success, the program prints the account ID and the welcome email ID.
Stop the worker
Press Ctrl-C in the worker terminal. The worker drains its running handlers for up to 30 seconds, then exits.
Next
- See what you just ran in Tasks and Runs.
- Learn how a handler survives a failure in Steps.