> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zeroruntime.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Run Your Agent

> Register and run your agent with serve, start sessions with invoke or the Dispatch API, and connect it to the phone network.

`zeroruntime.serve()` runs your agent as a **worker**: it registers the agent with Zero Runtime
and waits. Each time you **start a session**, the worker serves it with a fresh instance of your
agent. How you start one depends on where you are:

* **Local development:** call `zeroruntime.invoke()` from your code, or run
  `zeroruntime invoke` from the CLI. Both open a playground link you can talk to.
* **Production:** deploy your worker to [Zero Runtime Cloud](/runtime-sdk/deployments/zeroruntime-cloud)
  or [self-host](/runtime-sdk/deployments/self-hosting) it. Sessions then start from phone calls: inbound calls reach your agent through a
  [routing rule](#connect-to-the-phone-network), and you place outbound calls with the
  `/v2/sip/call` API.

## Run it locally

During development, start the worker and fire `zeroruntime.invoke()` from `on_ready`. As soon
as the worker registers, it opens a playground session and prints a link you can open to talk
to your agent.

<CodeGroup>
  ```python main.py theme={null}
  import zeroruntime
  from zeroruntime import Agent, Pipeline
  from zeroruntime.inference import AICousticsDenoise, TurnDetector, DeepgramSTT, GoogleLLM, CartesiaTTS

  pipeline = Pipeline(
      stt=DeepgramSTT(),
      llm=GoogleLLM(),
      tts=CartesiaTTS(),
      turn_detector=TurnDetector(model="echo-large"),
      denoise=AICousticsDenoise(model_id="quail-vf-2.2-l-16khz"),
  )


  class Assistant(Agent):
      def __init__(self) -> None:
          super().__init__(
              instructions="You are a helpful assistant.",
              pipeline=pipeline,
          )

      async def on_enter(self):
          await self.session.say("Hi! How can I help?")


  if __name__ == "__main__":
      # Pass the class itself (not an instance): serve() builds a fresh Assistant +
      # pipeline per call, which is required for correct per-call state under concurrent calls.
      zeroruntime.serve(Assistant, on_ready=lambda: zeroruntime.invoke(room=zeroruntime.Room(playground=True)))
  ```

  ```typescript agent.ts theme={null}
  import 'dotenv/config';
  import * as zeroruntime from '@zeroruntime/js-sdk';
  import { Agent, Pipeline } from '@zeroruntime/js-sdk';
  import { AICousticsDenoise, TurnDetector, DeepgramSTT, GoogleLLM, CartesiaTTS } from '@zeroruntime/js-sdk/inference';

  const pipeline = Pipeline({
    stt: DeepgramSTT(),
    llm: GoogleLLM(),
    tts: CartesiaTTS(),
    turn_detector: TurnDetector({ model: 'echo-large' }),
    denoise: AICousticsDenoise({ model_id: 'quail-vf-2.2-l-16khz' }),
  });

  class Assistant extends Agent {
    constructor() {
      super({
        instructions: 'You are a helpful assistant.',
        pipeline,
      });
    }

    async on_enter() {
      await this.session!.say('Hi! How can I help?');
    }
  }

  // Pass the class itself (not an instance): serve() builds a fresh Assistant +
  // pipeline per call, which is required for correct per-call state under concurrent calls.
  await zeroruntime.serve(Assistant, {
    on_ready: () => zeroruntime.invoke({ room: Room({ playground: true }) }),
  });
  ```
</CodeGroup>

Start the worker:

<CodeGroup>
  ```bash Python theme={null}
  python main.py
  ```

  ```bash Node JS theme={null}
  npx tsx agent.ts
  ```
</CodeGroup>

`serve()` blocks and keeps the worker running, accepting a fresh session for every caller until
you stop it. Code after `serve()` never runs, so use `on_ready` for anything that should happen
once the worker is up. To scale out, run more workers with the same agent ID; the runtime
load-balances sessions across them.

## Go to production

`on_ready` + `invoke()` is only for local testing. Remove it before you deploy, or every worker
you start dispatches the agent into a new room on startup. In production, sessions come from the
`zeroruntime invoke` CLI, or
[phone calls](#connect-to-the-phone-network).

<CodeGroup>
  ```python main.py theme={null}
  if __name__ == "__main__":
      zeroruntime.serve(Assistant, on_ready=lambda: zeroruntime.invoke(room=zeroruntime.Room(playground=True)))  # [!code --]
      zeroruntime.serve(Assistant)  # [!code ++]
  ```

  ```typescript agent.ts theme={null}
  await zeroruntime.serve(Assistant, { // [!code --]
    on_ready: () => zeroruntime.invoke({ room: Room({ playground: true }) }), // [!code --]
  }); // [!code --]
  await zeroruntime.serve(Assistant, {}); // [!code ++]
  ```
</CodeGroup>

Then choose where your workers run:

<CardGroup cols={2}>
  <Card title="Zero Runtime Cloud" icon="cloud" href="/runtime-sdk/deployments/zeroruntime-cloud">
    One command deploys your agent. Zero Runtime builds, hosts, and scales it for you.
  </Card>

  <Card title="Self-Hosting" icon="server" href="/runtime-sdk/deployments/self-hosting">
    Run the worker on your own Docker host, VM, or Kubernetes cluster.
  </Card>
</CardGroup>

## Place a phone call

Pass `Sip(...)` instead of a playground room to start the session on an outbound call. You
need an [outbound gateway](#connect-to-the-phone-network) set up first.

<CodeGroup>
  ```python Python theme={null}
  import zeroruntime

  zeroruntime.invoke(
      sip=zeroruntime.Sip(call_to="+1XXXXXXXXXX", call_from="+1XXXXXXXXXX"),
  )
  ```

  ```typescript Node JS theme={null}
  import * as zeroruntime from '@zeroruntime/js-sdk';
  import { Sip } from '@zeroruntime/js-sdk';

  await zeroruntime.invoke({
    sip: Sip({ call_to: '+1XXXXXXXXXX', call_from: '+1XXXXXXXXXX' }),
  });
  ```
</CodeGroup>

## Connect to the phone network

To take or place phone calls, set up gateways in the Zero Runtime dashboard or API, and a
routing rule that points at your agent ID. Your worker must be running to receive calls.

<AccordionGroup>
  <Accordion title="Set up inbound calls" icon="phone-arrow-down-left">
    **1. Create an inbound gateway**: the entry point for incoming calls.

    <Tabs>
      <Tab title="Dashboard">
        1. Copy the **Inbound URL** from the Zero Runtime dashboard.
        2. Go to **Twilio** and create a new SIP Trunk.
        3. Go to the **Origination** section, paste the Inbound URL there, and save it.

        <Frame>
          <video autoPlay loop muted playsInline controls className="w-full aspect-video rounded-xl" src="https://assets.videosdk.live/images/setup.mp4" />
        </Frame>
      </Tab>

      <Tab title="API">
        ```bash theme={null}
        curl --request POST \
          --url https://api.videosdk.live/v2/sip/inbound-gateways \
          --header 'Authorization: YOUR_ZERORUNTIME_AUTH_TOKEN' \
          --header 'Content-Type: application/json' \
          --data '{ "name": "My Inbound Gateway", "numbers": ["+1234567890"] }'
        ```

        Then set the **origination URI** of your Twilio SIP trunk to the inbound gateway hostname (shown in the dashboard), e.g. `sip:9WXXXXXXX.sip.videosdk.live`.
      </Tab>
    </Tabs>

    **2. Create a routing rule**: connects the inbound gateway to your agent.

    <Tabs>
      <Tab title="Dashboard">
        1. From your inbound gateway, click **Configure rule**, then **Create new routing rule**.
        2. Enter a **Routing Rule Name** and select **API Key** authentication.
        3. Choose the **Call Direction** (Inbound).
        4. Enter the **Phone Number** and select a **Room Type**.
        5. Enter your `ZERORUNTIME_AGENT_ID` as the **Agent ID** and click **Save**.

        <Frame>
          <video autoPlay loop muted playsInline controls className="w-full aspect-video rounded-xl" src="https://assets.videosdk.live/images/routing-rules.mp4" />
        </Frame>
      </Tab>

      <Tab title="API">
        ```bash theme={null}
        curl --request POST \
          --url https://api.videosdk.live/v2/sip/routing-rule \
          --header 'Authorization: YOUR_ZERORUNTIME_AUTH_TOKEN' \
          --header 'Content-Type: application/json' \
          --data '{
            "gatewayId": "gateway_in_123456789",
            "name": "Support Line Rule",
            "numbers": ["+1234567890"],
            "agentId": "my-agent"
          }'
        ```
      </Tab>
    </Tabs>

    **3. Test it**: with your worker running, dial your provider's phone number from any phone. The agent greets you as soon as the call connects.

    <Tip>
      Using a different SIP provider? Follow its inbound setup:
      [Telnyx](/telephony/integrations/telnyx),
      [Vonage](/telephony/integrations/vonage#configuring-inbound-calling), or
      [Plivo](/telephony/integrations/plivo).
    </Tip>
  </Accordion>

  <Accordion title="Set up outbound calls" icon="phone-arrow-up-right">
    **1. Create an outbound gateway**: lets the agent dial out to the PSTN.

    <Tabs>
      <Tab title="Dashboard">
        1. In Twilio, open your SIP Trunk and go to the **Termination** section. Create a **Termination URI** and add username/password credentials.
        2. Back in the Zero Runtime Dashboard, open the outbound gateway configuration.
        3. Paste the Twilio **Termination URI** as the address, add the same **username/password** credentials, and save.

        <Frame>
          <video autoPlay loop muted playsInline controls className="w-full aspect-video rounded-xl" src="https://assets.videosdk.live/images/outbound-gateway.mp4" />
        </Frame>
      </Tab>

      <Tab title="API">
        ```bash theme={null}
        curl --request POST \
          --url https://api.videosdk.live/v2/sip/outbound-gateways \
          --header 'Authorization: YOUR_ZERORUNTIME_AUTH_TOKEN' \
          --header 'Content-Type: application/json' \
          --data '{
            "name": "My Outbound Gateway",
            "numbers": ["+12065551234"],
            "address": "sip.myprovider.com",
            "transport": "udp",
            "auth": { "username": "your-username", "password": "your-password" }
          }'
        ```

        Set `address` and `auth` to match your Twilio **Termination URI** and credentials so outbound calls reach the PSTN.
      </Tab>
    </Tabs>

    **2. Place a call**: with your worker running, trigger a call with the [SDK](#place-a-phone-call) or the API. Replace `YOUR_ZERORUNTIME_AUTH_TOKEN` and `routingRuleId` with your own.

    ```bash theme={null}
    curl --request POST \
      --url https://api.videosdk.live/v2/sip/call \
      --header 'Authorization: YOUR_ZERORUNTIME_AUTH_TOKEN' \
      --header 'Content-Type: application/json' \
      --data '{
        "sipCallFrom": "+14155550100",
        "sipCallTo": "+14155550199",
        "routingRuleId": "rr_2554md"
      }'
    ```

    <Tip>
      For lower latency, run your agent in the same region as your SIP provider.
    </Tip>

    <Tip>
      Using a different SIP provider? Follow its outbound setup:
      [Telnyx](/telephony/integrations/telnyx),
      [Vonage](/telephony/integrations/vonage#configuring-outbound-calling), or
      [Plivo](/telephony/integrations/plivo).
    </Tip>
  </Accordion>
</AccordionGroup>

## Troubleshooting

| Symptom | Cause | Fix |
| :- | :- | :- |
| Dispatch returns `No workers have registered with agentId '...'` | No running worker uses that ID. Usually `ZERORUNTIME_AGENT_ID` is unset (the worker registered as `zeroruntime_agent`) or doesn't exactly match. | Set `ZERORUNTIME_AGENT_ID` in `.env`, restart the worker, and use the same ID in dispatch and routing rules. |
| Dispatch returns `No agent is deployed with agentId '...'` | Zero Runtime Cloud: the agent exists but has no deployed version. | Run `zeroruntime up`. See [Zero Runtime Cloud](/runtime-sdk/deployments/zeroruntime-cloud). |
| Dispatch returns `No workers available` | No worker is available to take the session. | Check that a worker is running, or add workers if they're all at capacity. |
| A new room opens every time a worker starts | `on_ready` still calls `invoke()`. | Remove it for production. See [Go to production](#go-to-production). |
| Phone calls never reach the agent | The routing rule's agent ID doesn't match `ZERORUNTIME_AGENT_ID`, or no worker is running. | Fix the rule's agent ID and make sure the worker is up. |

## What's Next

<CardGroup cols={2}>
  <Card title="Create an Agent" icon="robot" href="/runtime-sdk/build/creating-an-agent">
    Define the agent that serve runs.
  </Card>

  <Card title="Tools and Capabilities" icon="wrench" href="/runtime-sdk/build/tools-and-capabilities/overview">
    Give the agent function tools and external services.
  </Card>

  <Card title="Telephony" icon="phone" href="/runtime-sdk/build/telephony/overview">
    DTMF, call transfer, and voicemail detection.
  </Card>

  <Card title="Deploy to Zero Runtime Cloud" icon="rocket" href="/runtime-sdk/deployments/zeroruntime-cloud">
    Take your agent to production.
  </Card>
</CardGroup>

## References

<Tabs>
  <Tab title="Python">
    #### Examples

    <CardGroup cols={2}>
      <Card title="Cascade Basic" icon="github" href="https://github.com/ZeroRuntimeAI/zeroruntime-python-examples/blob/main/getting_started/cascade_basic.py">
        Smallest complete agent showing serve + invoke.
      </Card>
    </CardGroup>
  </Tab>

  <Tab title="Node JS">
    #### Examples

    <CardGroup cols={2}>
      <Card title="Cascade Basic" icon="github" href="https://github.com/ZeroRuntimeAI/zeroruntime-js-examples/blob/main/getting_started/cascade_basic.ts">
        Smallest complete agent showing serve + invoke.
      </Card>
    </CardGroup>
  </Tab>
</Tabs>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.