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

# Deploy API Server

> Deploy the ouroborai agent API to Railway

This guide walks through deploying the Hono API server to Railway with Docker,
including Redis for persistent state management.

## Prerequisites

* A [Railway](https://railway.app) account
* The ouroborai monorepo cloned locally
* An Arbitrum RPC URL (Alchemy, Infura, or public)
* An Anthropic API key for the agent runner
* A funded wallet private key (ETH for gas, USDC for x402 settlement)

## Deployment

<Steps>
  <Step title="Create a Railway project">
    Log into Railway and create a new project. Select **Deploy from GitHub repo**
    and connect the ouroborai repository. Railway detects the `Dockerfile` in the
    project root automatically.

    If you prefer manual deployment, install the Railway CLI:

    ```bash theme={"theme":{"light":"dracula","dark":"dracula"}}
    npm install -g @railway/cli
    railway login
    railway init
    ```
  </Step>

  <Step title="Configure environment variables">
    In the Railway dashboard, navigate to your service and open the **Variables**
    tab. Add the following required variables:

    ```bash theme={"theme":{"light":"dracula","dark":"dracula"}}
    PRIVATE_KEY=0xYOUR_WALLET_PRIVATE_KEY
    ANTHROPIC_API_KEY=sk-ant-your-key
    ARB_RPC_URL=https://arb-mainnet.g.alchemy.com/v2/YOUR_KEY
    ```

    <Warning>
      `PRIVATE_KEY` controls the agent's on-chain wallet. Use a dedicated hot
      wallet with limited funds -- never your primary wallet.
    </Warning>

    Add optional variables for full functionality:

    ```bash theme={"theme":{"light":"dracula","dark":"dracula"}}
    # Redis (provision a Redis add-on in Railway, or use an external URL)
    REDIS_URL=redis://default:password@host:port

    # x402 payment recipient address
    PAY_TO_ADDRESS=0xYOUR_REVENUE_ADDRESS

    # CORS origin for your web frontend
    CORS_ORIGIN=https://app.ouroborai.com

    # Disable x402 payments in staging
    SKIP_PAYMENT=false

    # Server port (Railway injects PORT automatically)
    PORT=3000
    ```
  </Step>

  <Step title="Provision Redis (optional but recommended)">
    Redis enables persistent job queues, conversation threads, and x402 nonce
    tracking across server restarts. Without it, these stores fall back to
    in-memory and reset on each deploy.

    In Railway, click **New Service** and select **Redis**. Railway automatically
    injects `REDIS_URL` into your service environment.

    The API server uses these Redis key prefixes:

    | Prefix          | Purpose                             |
    | --------------- | ----------------------------------- |
    | `arb:job:*`     | Job queue entries                   |
    | `arb:thread:*`  | Conversation threads                |
    | `arb:nonce:*`   | x402 nonce deduplication (24hr TTL) |
    | `arb:revenue:*` | Payment tracking and revenue stats  |
  </Step>

  <Step title="Review the Dockerfile">
    The project ships a multi-stage Dockerfile that builds all workspace
    packages and produces a minimal production image:

    ```dockerfile theme={"theme":{"light":"dracula","dark":"dracula"}}
    FROM oven/bun:1 AS builder
    WORKDIR /app
    COPY . .
    RUN bun install --frozen-lockfile
    RUN bun run build

    FROM oven/bun:1-slim
    WORKDIR /app
    COPY --from=builder /app/apps/api/dist ./dist
    COPY --from=builder /app/node_modules ./node_modules
    EXPOSE 3000
    CMD ["bun", "run", "dist/index.js"]
    ```

    Build order matters because workspace packages have dependencies:
    `sdk` builds first, then `adapters`, `skills`, `timeboost`, and
    `smart-account`, and finally the `api` app.

    <Note>
      `@zerodev/*`, `permissionless`, and `tslib` must be marked as external
      in the Bun build configuration to avoid bundling issues.
    </Note>
  </Step>

  <Step title="Deploy">
    Push to your connected branch (or trigger a deploy from the CLI):

    ```bash theme={"theme":{"light":"dracula","dark":"dracula"}}
    railway up
    ```

    Railway builds the Docker image, starts the container, and assigns a
    public URL.
  </Step>

  <Step title="Verify the deployment">
    Hit the health endpoint to confirm the server is running:

    ```bash theme={"theme":{"light":"dracula","dark":"dracula"}}
    curl https://your-service.railway.app/health
    ```

    Expected response:

    ```json theme={"theme":{"light":"dracula","dark":"dracula"}}
    { "ok": true, "ts": 1709337600000 }
    ```

    Also check the root endpoint for version and chain info:

    ```bash theme={"theme":{"light":"dracula","dark":"dracula"}}
    curl https://your-service.railway.app/
    ```

    ```json theme={"theme":{"light":"dracula","dark":"dracula"}}
    {
      "name": "arbitrum-agent",
      "version": "0.1.0",
      "chain": "arbitrum-one",
      "chainId": 42161
    }
    ```
  </Step>

  <Step title="Test the agent">
    Submit a prompt to verify end-to-end functionality:

    ```bash theme={"theme":{"light":"dracula","dark":"dracula"}}
    curl -X POST https://your-service.railway.app/agent/prompt \
      -H "Content-Type: application/json" \
      -d '{"prompt": "What is my ETH balance?"}'
    ```

    You should receive a `202` response with a `jobId` and `threadId`. Poll
    the job status endpoint to retrieve the agent's answer:

    ```bash theme={"theme":{"light":"dracula","dark":"dracula"}}
    curl https://your-service.railway.app/agent/job/JOB_ID
    ```
  </Step>
</Steps>

## Troubleshooting

<AccordionGroup>
  <Accordion title="502 Bad Gateway on deploy">
    Railway needs the server to bind to `0.0.0.0`, not `localhost`. The ouroborai
    API server already binds to `0.0.0.0` by default. Ensure the `PORT`
    environment variable matches what Railway expects (it injects this
    automatically).
  </Accordion>

  <Accordion title="Redis connection refused">
    If using Railway's Redis add-on, ensure the `REDIS_URL` variable is linked
    to the correct service. The API server falls back to in-memory stores
    gracefully if Redis is unreachable, so the server will start even if Redis
    is misconfigured.
  </Accordion>

  <Accordion title="x402 payments failing">
    Verify that `PAY_TO_ADDRESS` is a valid Arbitrum One address and that the
    calling wallet has sufficient USDC. Set `SKIP_PAYMENT=true` for staging
    environments where you want to bypass payment verification.
  </Accordion>
</AccordionGroup>
