Skip to main content
This guide gets you from zero to a working Voyage in under a minute of reading. For the conceptual overview, see Voyages. Working through this with a coding agent? Point it at the Sail skills package and ask:
Build a small background agent on Sail with Voyage telemetry. Keep the
agent harness simple, use the sail package, wrap the run with
sail.voyage.run(..., version=1), use @sail.agent and @sail.span for
attribution, make the dashboard trace show model calls, and attribute Sailbox
execs only if the workload already uses a Sailbox.

Prerequisites

  • Python 3.9+
  • A Sail API key
  • The Sail SDK: pip install sail

Step 1: install + auth

pip install sail
export SAIL_API_KEY=sk_your_key_here
SAIL_API_KEY always wins. When it is unset, the SDK uses the credential stored by sail auth login under ~/.sail.

Step 2: write the minimal Voyage

# voyage_hello.py
import sail

@sail.agent("Researcher")
@sail.span("say hello")
def say_hello():
    sail.voyage.event("hello.fired", payload={"message": "hi from sail"})
    response = sail.inference.responses.create(
        model="zai-org/GLM-5.2-FP8",
        input="In one sentence, say hello.",
        background=False,
    )
    sail.voyage.event("inference.done", payload={"response_id": response["id"]})


with sail.voyage.run(
    name="hello-voyage",
    version=1,
    metadata={"example": "quickstart"},
):
    say_hello()

print(f"Open: {sail.voyage.dashboard_url()}")
run() emits voyage.completed when the block exits cleanly. If the block raises, it emits voyage.failed and re-raises. No try/except needed.

Step 3: run it

python voyage_hello.py
You should see one line printed:
Open: https://app.sailresearch.com/prod/voyages/voy_...

Step 4: verify in the dashboard

Open the printed URL. You should see:
  • Status: voyage completed
  • Agents: 1 (Researcher)
  • Events: 2 (hello.fired, inference.done)
  • Model calls: 1 (zai-org/GLM-5.2-FP8, status completed)
  • Execution Trace: one agent block with one span, two events nested underneath, one model row.
You now have a Voyage: a dashboard trace for a background agent run, including events, model-call attribution, and terminal status.

Decorators

For function-shaped work, decorate instead of nesting context managers. You get the same events, spans, and attribution:
import sail

@sail.agent("Researcher")
@sail.span()
def say_hello():
    sail.voyage.event("greeting.sent", payload={"channel": "stdout"})

with sail.voyage.run(name="hello-voyage", version=1):
    say_hello()
Calls you do not wrap are still captured. Sail inference and Sailbox execs made inside a Voyage get automatic, timed spans named after your calling code and marked “auto” in the dashboard.

What to do next

  • Add a Sailbox: see the Sailboxes guide for creating a long-running sandboxed VM, then pass sailbox_id=sb.sailbox_id to sail.voyage.create() so the Voyage is bound to that Sailbox.
  • Prefer an agent-guided setup? Use the Sail skills package and ask your coding agent to build or instrument a background agent with Voyage telemetry.
  • Add a second agent: see Voyages Patterns → Multi-agent.
  • Something looking wrong? Install the Sail skills and use the sail-voyage-debugging skill.

Common first-run gotchas

  • Voyage doesn’t appear in dashboard. Make sure SAIL_API_KEY is set to a valid Sail API key (sk_...). Events are recorded against the key’s org, so a missing or wrong-org key means nothing shows up.
  • Process exits without a terminal event. The Voyage stays “in progress” forever (a bounded best-effort flush at exit delivers trailing events, but can still drop them if the network is down. It also never marks the Voyage terminal). Use with sail.voyage.run(...) so the terminal state is emitted for you, or call voyage.complete() / voyage.fail() yourself.
  • Unexpected “auto” spans. If a Sail inference call or Sailbox exec runs inside a Voyage with no active span, the SDK creates a timed span for it automatically. That is expected. Add @sail.span(...) only when you want to choose the step name yourself.
  • CERTIFICATE_VERIFY_FAILED on macOS Python. Some python.org installs do not have a usable root CA bundle. Install certifi and point Python at it: python -m pip install certifi, then export SSL_CERT_FILE="$(python -c 'import certifi; print(certifi.where())')".
  • Unsupported model. Use a model id from Sail, not necessarily the model your coding agent is using. Start with zai-org/GLM-5.2-FP8; to list your account’s available models, call GET https://api.sailresearch.com/v1/models with your SAIL_API_KEY.