Get started / Quickstart
Quickstart
Install an SDK, create an API key, and delegate your first piece of work. By the end you will have created a mandate, watched what the agent did, and waited for it to finish.
Every code sample on this site is available in TypeScript and Go. Use the language selector on any sample to switch all of them at once.
Shadway is invite-only right now. You need an invite to get an API key and the SDKs, which aren't published publicly yet. If you don't have access, request an invite and come back once you're in. We're aiming for a public release and general availability by October 2026.
Before you begin
You need a Shadway API key, which comes with your invite. A test key
(sk_test_...) creates only test-mode resources, and a live key (sk_live_...)
creates live ones. The key decides the mode. There is no separate setting.
You also need a model for the agent to reason with. The samples read it from
SHADWAY_MODEL. Set it to a provider-qualified model that your deployment
supports.
1. Install an SDK
While Shadway is invite-only, install from the source in your invite. The commands below are how you'll install once the packages are public.
npm install shadwayThe TypeScript SDK requires Node.js 20 or later and works in any runtime with the Web Crypto API.
go get github.com/shadwayhq/shadway-goThe Go SDK requires Go 1.24 or later. Listing and streaming use range-over-func iterators.
2. Authenticate
Both SDKs read SHADWAY_API_KEY from the environment:
export SHADWAY_API_KEY="sk_test_..."
export SHADWAY_MODEL="your-provider/your-model"Keep keys out of source control. The SDKs also accept the key directly in the client constructor if your secrets live elsewhere.
3. Create an agent
An agent defines the model, instructions, and capabilities that mandates will use.
Capabilities are named per action: email.send, not email.
import { Shadway } from "shadway";
const shadway = new Shadway();
const model = process.env.SHADWAY_MODEL;
if (!model) throw new Error("Set SHADWAY_MODEL");
const agent = await shadway.agents.create({
name: "Assistant",
model,
instructions: "Be concise. Report missing information instead of guessing.",
capabilities: ["email.send", "email.get_delivery"],
});
console.log(agent.id); // Save this ID in your application.package main
import (
"context"
"fmt"
"log"
"os"
shadway "github.com/shadwayhq/shadway-go"
)
func main() {
client, err := shadway.New()
if err != nil {
log.Fatal(err)
}
model := os.Getenv("SHADWAY_MODEL")
if model == "" {
log.Fatal("Set SHADWAY_MODEL")
}
instructions := "Be concise. Report missing information instead of guessing."
agent, err := client.Agents.Create(context.Background(), shadway.AgentCreateParams{
Name: "Assistant",
Model: model,
Instructions: &instructions,
Capabilities: []string{"email.send", "email.get_delivery"},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(agent.ID) // Save this ID in your application.
}The agent is reusable. You create it once and start many mandates against it, each with its own objective and permissions.
4. Delegate work
A mandate is work you delegated: an objective, plus the authority that bounds it. Replace the address below with a real contact before running the example.
const mandate = await shadway.mandates.create({
agent: agent.id,
objective:
"Email sales@atlas.example and ask for a quote on 200 units of " +
"part #48-221, including shipping cost and delivery date. " +
"Report the quoted price and delivery date.",
authority: {
capabilities: ["email.send", "email.get_delivery"],
maximumActions: 5,
},
});
console.log(mandate.id, mandate.status);maxActions := int64(5)
mandate, err := client.Mandates.Create(ctx, shadway.MandateCreateParams{
Agent: agent.ID,
Objective: "Email sales@atlas.example and ask for a quote on 200 units of " +
"part #48-221, including shipping cost and delivery date. " +
"Report the quoted price and delivery date.",
Authority: &shadway.Authority{
Capabilities: []string{"email.send", "email.get_delivery"},
MaximumActions: &maxActions,
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(mandate.ID, mandate.Status)Shadway enforces the authority on every action: this mandate can send email and check delivery, take at most five consequential actions, and nothing else. The objective and any guidance steer the agent but never enforce anything.
5. Wait for the result
The mandate runs on Shadway's servers. Waiting only observes it. Ending the wait, or your process, does not stop the work.
import { ShadwayTimeoutError } from "shadway";
const work = shadway.mandate(mandate.id);
try {
const result = await work.wait({ timeout: "15m" });
console.log(result.status);
console.log(result.currentState.summary);
} catch (error) {
if (!(error instanceof ShadwayTimeoutError)) throw error;
console.log("Still working. Reconnect later with mandate ID:", work.id);
}result, err := client.Mandate(mandate.ID).Wait(ctx, shadway.WaitOptions{
Timeout: 15 * time.Minute,
})
if err != nil {
if shadway.IsTimeout(err) {
fmt.Println("Still working. Reconnect later with mandate ID:", mandate.ID)
return
}
log.Fatal(err)
}
fmt.Println(result.Status)
fmt.Println(result.CurrentState.Summary)wait returns when the mandate reaches completed, failed, canceled, or
expired. A timeout only ends the local wait. If the supplier replies while you
are away, the agent handles it. If the wait for a reply times out, the agent
reasons about what to do next.
What you built
You created a reusable agent, delegated one bounded piece of work to it, and observed the outcome. Shadway checked every action against the mandate's authority and recorded each one with its result.