Getting Started
Set up AgentOwl from scratch — install the CLI, authenticate, create tenants, bind channels, and route inbound messages.
This guide walks you through setting up AgentOwl from scratch — installing the CLI, authenticating, creating your first tenant, provisioning communication channels, and routing inbound messages to your application.
Prerequisites
- Node.js 18+ (ES module support required)
- npm (included with Node.js)
- A running AgentOwl Worker instance (local or deployed)
For local development, you also need:
- Wrangler CLI (
npm install -g wrangler) for running the Cloudflare Worker locally - Provider credentials (Telnyx API key, Cloudflare API token) if you want to test real inbound messages
1. Install the CLI
From source (development)
git clone <repo-url> && cd agentowl-dev
npm install
npm run build
# Link the CLI globally
cd packages/cli
npm linkAfter linking, the agentowl command is available globally.
Run without installing
cd packages/cli
npm run dev -- <command> [args]Verify installation
agentowl --version
# 0.1.02. Start the Worker locally
cd packages/worker
cp .dev.vars.example .dev.varsEdit .dev.vars with your provider credentials:
TELNYX_PUBLIC_KEY=<your-telnyx-public-key>
TELNYX_API_KEY=<your-telnyx-api-key>
CLOUDFLARE_API_TOKEN=<your-cloudflare-api-token>
CLOUDFLARE_ACCOUNT_ID=<your-cloudflare-account-id>
AGENTOWL_INGRESS_BASE_URL=http://localhost:8787Start the local development server:
npm run dev
# Worker running at http://localhost:8787The Worker automatically creates the D1 database and applies migrations on first run.
3. Authenticate
You need a bearer token to interact with the API. In a production deployment, tokens are provisioned by a platform admin. For local development, you can create one directly in KV.
Option A: CLI login
agentowl login --token <your-bearer-token> --url http://localhost:8787
# Config saved to ~/.agentowl/config.jsonOption B: Environment variables
export AGENTOWL_API_TOKEN=<your-bearer-token>
export AGENTOWL_API_URL=http://localhost:8787Option C: Config file
mkdir -p ~/.agentowl
cat > ~/.agentowl/config.json <<'EOF'
{
"token": "<your-bearer-token>",
"api_url": "http://localhost:8787"
}
EOF
chmod 600 ~/.agentowl/config.jsonVerify connectivity
agentowl versionIf the server is reachable, this prints both the CLI and server versions.
4. Create a tenant (platform admin)
Tenants are the top-level isolation boundary. Every binding, endpoint, route, and policy belongs to exactly one tenant. Only platform admins can create tenants.
agentowl tenant create acme --display-name "Acme Corp"Output:
tenant_id t_abc123...
slug acme
display_name Acme Corp
status active
created_at 2026-03-24T12:00:00ZPreview without creating:
agentowl tenant create acme --display-name "Acme Corp" --dry-runList all tenants:
agentowl tenant list5. Create an endpoint
An endpoint is where inbound messages are delivered. The most common type is a webhook — a URL in your application that receives POST requests with the normalized event payload.
agentowl endpoint create webhook my-webhook https://app.acme.com/hooks/inbound \
--tenant acmeOther endpoint types: agent-runtime, queue, sip, email-forward, hosted-page, dead-letter.
List endpoints:
agentowl endpoint list --tenant acme6. Bind a communication channel
Bindings connect a communication identity (email address, phone number) to a tenant. Once bound and provisioned, inbound messages to that identity flow through AgentOwl.
Bind an email address
agentowl email bind acme support@acme.comBind a domain (catch-all)
agentowl email domain add acme acme.comThis creates a binding for *@acme.com.
Bind an SMS number
agentowl messaging bind acme --number +15551234567 --capability smsAuto-assign an SMS number
agentowl messaging bind acme --capability sms voiceWhen no --number is specified, AgentOwl requests an auto-assigned number from the provider.
Import an existing phone number
agentowl messaging import-number acme +15559876543 --capability sms voiceList all bindings for a tenant:
agentowl email inspect <binding_id>
agentowl messaging inspect <binding_id>7. Create a route
Routes connect bindings to endpoints. When an inbound message arrives on a binding, the route determines which endpoint receives it.
agentowl route create \
--tenant acme \
--binding <binding_id> \
--endpoint <endpoint_id> \
--priority 100Lower priority numbers are evaluated first. Routes can have a fallback policy (dead-letter, drop, or quarantine) for when the endpoint fails.
List routes:
agentowl route list --tenant acme8. Publish the runtime config
After creating your bindings, endpoints, and routes, publish the runtime configuration so the ingress workers can use it:
agentowl config publish acmeThis snapshots the current desired state and distributes it to KV for all ingress workers.
9. Verify with doctor
Run a health check to confirm everything is correctly configured:
agentowl doctor acmeThis checks API connectivity, tenant status, published config, and provider drift.
Declarative configuration (apply)
Instead of running individual commands, you can define your entire configuration in a YAML file and apply it in one step.
Create acme.yaml:
tenant:
slug: acme
display_name: "Acme Corp"
endpoints:
- name: main-webhook
type: webhook
config:
url: https://app.acme.com/hooks/inbound
bindings:
- address_or_number: support@acme.com
provider: cloudflare
channels:
- email
capabilities:
- email
- address_or_number: "+15551234567"
provider: telnyx
channels:
- sms
capabilities:
- sms
routes:
- binding_address: support@acme.com
endpoint_name: main-webhook
priority: 100
fallback_policy: dead-letter
- binding_address: "+15551234567"
endpoint_name: main-webhook
priority: 100
fallback_policy: dead-letterValidate
agentowl validate -f acme.yamlPreview changes
agentowl plan -f acme.yamlApply
agentowl apply -f acme.yaml --auto-approveThis creates the tenant (if needed), endpoints, bindings, routes, and publishes the runtime config.
What's next
- Roles and Permissions — understand platform admin vs tenant admin access
- CLI Reference — complete command reference
- Local Development — practical setup for working on AgentOwl locally